--- /dev/null
+#FindBugs User Preferences\r
+#Sun Feb 20 00:21:29 JST 2011\r
+detectorAppendingToAnObjectOutputStream=AppendingToAnObjectOutputStream|true\r
+detectorBadAppletConstructor=BadAppletConstructor|false\r
+detectorBadResultSetAccess=BadResultSetAccess|true\r
+detectorBadSyntaxForRegularExpression=BadSyntaxForRegularExpression|true\r
+detectorBadUseOfReturnValue=BadUseOfReturnValue|true\r
+detectorBadlyOverriddenAdapter=BadlyOverriddenAdapter|true\r
+detectorBooleanReturnNull=BooleanReturnNull|true\r
+detectorCallToUnsupportedMethod=CallToUnsupportedMethod|false\r
+detectorCheckImmutableAnnotation=CheckImmutableAnnotation|true\r
+detectorCheckTypeQualifiers=CheckTypeQualifiers|true\r
+detectorCloneIdiom=CloneIdiom|true\r
+detectorComparatorIdiom=ComparatorIdiom|true\r
+detectorConfusedInheritance=ConfusedInheritance|true\r
+detectorConfusionBetweenInheritedAndOuterMethod=ConfusionBetweenInheritedAndOuterMethod|true\r
+detectorCrossSiteScripting=CrossSiteScripting|true\r
+detectorDoInsideDoPrivileged=DoInsideDoPrivileged|true\r
+detectorDontCatchIllegalMonitorStateException=DontCatchIllegalMonitorStateException|true\r
+detectorDontIgnoreResultOfPutIfAbsent=DontIgnoreResultOfPutIfAbsent|true\r
+detectorDontUseEnum=DontUseEnum|true\r
+detectorDroppedException=DroppedException|true\r
+detectorDumbMethodInvocations=DumbMethodInvocations|true\r
+detectorDumbMethods=DumbMethods|true\r
+detectorDuplicateBranches=DuplicateBranches|true\r
+detectorEmptyZipFileEntry=EmptyZipFileEntry|true\r
+detectorEqualsOperandShouldHaveClassCompatibleWithThis=EqualsOperandShouldHaveClassCompatibleWithThis|true\r
+detectorFinalizerNullsFields=FinalizerNullsFields|true\r
+detectorFindBadCast2=FindBadCast2|true\r
+detectorFindBadForLoop=FindBadForLoop|true\r
+detectorFindCircularDependencies=FindCircularDependencies|false\r
+detectorFindDeadLocalStores=FindDeadLocalStores|true\r
+detectorFindDoubleCheck=FindDoubleCheck|true\r
+detectorFindEmptySynchronizedBlock=FindEmptySynchronizedBlock|true\r
+detectorFindFieldSelfAssignment=FindFieldSelfAssignment|true\r
+detectorFindFinalizeInvocations=FindFinalizeInvocations|true\r
+detectorFindFloatEquality=FindFloatEquality|true\r
+detectorFindHEmismatch=FindHEmismatch|true\r
+detectorFindInconsistentSync2=FindInconsistentSync2|true\r
+detectorFindJSR166LockMonitorenter=FindJSR166LockMonitorenter|true\r
+detectorFindLocalSelfAssignment2=FindLocalSelfAssignment2|true\r
+detectorFindMaskedFields=FindMaskedFields|true\r
+detectorFindMismatchedWaitOrNotify=FindMismatchedWaitOrNotify|true\r
+detectorFindNakedNotify=FindNakedNotify|true\r
+detectorFindNonSerializableStoreIntoSession=FindNonSerializableStoreIntoSession|true\r
+detectorFindNonSerializableValuePassedToWriteObject=FindNonSerializableValuePassedToWriteObject|true\r
+detectorFindNonShortCircuit=FindNonShortCircuit|true\r
+detectorFindNullDeref=FindNullDeref|true\r
+detectorFindNullDerefsInvolvingNonShortCircuitEvaluation=FindNullDerefsInvolvingNonShortCircuitEvaluation|true\r
+detectorFindOpenStream=FindOpenStream|true\r
+detectorFindPuzzlers=FindPuzzlers|true\r
+detectorFindRefComparison=FindRefComparison|true\r
+detectorFindReturnRef=FindReturnRef|true\r
+detectorFindRunInvocations=FindRunInvocations|true\r
+detectorFindSelfComparison=FindSelfComparison|true\r
+detectorFindSelfComparison2=FindSelfComparison2|true\r
+detectorFindSleepWithLockHeld=FindSleepWithLockHeld|true\r
+detectorFindSpinLoop=FindSpinLoop|true\r
+detectorFindSqlInjection=FindSqlInjection|true\r
+detectorFindTwoLockWait=FindTwoLockWait|true\r
+detectorFindUncalledPrivateMethods=FindUncalledPrivateMethods|true\r
+detectorFindUnconditionalWait=FindUnconditionalWait|true\r
+detectorFindUninitializedGet=FindUninitializedGet|true\r
+detectorFindUnrelatedTypesInGenericContainer=FindUnrelatedTypesInGenericContainer|true\r
+detectorFindUnreleasedLock=FindUnreleasedLock|true\r
+detectorFindUnsatisfiedObligation=FindUnsatisfiedObligation|true\r
+detectorFindUnsyncGet=FindUnsyncGet|true\r
+detectorFindUselessControlFlow=FindUselessControlFlow|true\r
+detectorFormatStringChecker=FormatStringChecker|true\r
+detectorHugeSharedStringConstants=HugeSharedStringConstants|true\r
+detectorIDivResultCastToDouble=IDivResultCastToDouble|true\r
+detectorIncompatMask=IncompatMask|true\r
+detectorInconsistentAnnotations=InconsistentAnnotations|true\r
+detectorInefficientMemberAccess=InefficientMemberAccess|false\r
+detectorInefficientToArray=InefficientToArray|true\r
+detectorInfiniteLoop=InfiniteLoop|true\r
+detectorInfiniteRecursiveLoop=InfiniteRecursiveLoop|true\r
+detectorInfiniteRecursiveLoop2=InfiniteRecursiveLoop2|false\r
+detectorInheritanceUnsafeGetResource=InheritanceUnsafeGetResource|true\r
+detectorInitializationChain=InitializationChain|true\r
+detectorInstantiateStaticClass=InstantiateStaticClass|true\r
+detectorInvalidJUnitTest=InvalidJUnitTest|true\r
+detectorIteratorIdioms=IteratorIdioms|true\r
+detectorLazyInit=LazyInit|true\r
+detectorLoadOfKnownNullValue=LoadOfKnownNullValue|true\r
+detectorLostLoggerDueToWeakReference=LostLoggerDueToWeakReference|true\r
+detectorMethodReturnCheck=MethodReturnCheck|true\r
+detectorMultithreadedInstanceAccess=MultithreadedInstanceAccess|true\r
+detectorMutableLock=MutableLock|true\r
+detectorMutableStaticFields=MutableStaticFields|true\r
+detectorNaming=Naming|true\r
+detectorNumberConstructor=NumberConstructor|true\r
+detectorOverridingEqualsNotSymmetrical=OverridingEqualsNotSymmetrical|true\r
+detectorPreferZeroLengthArrays=PreferZeroLengthArrays|true\r
+detectorPublicSemaphores=PublicSemaphores|false\r
+detectorQuestionableBooleanAssignment=QuestionableBooleanAssignment|true\r
+detectorReadOfInstanceFieldInMethodInvokedByConstructorInSuperclass=ReadOfInstanceFieldInMethodInvokedByConstructorInSuperclass|true\r
+detectorReadReturnShouldBeChecked=ReadReturnShouldBeChecked|true\r
+detectorRedundantInterfaces=RedundantInterfaces|true\r
+detectorRepeatedConditionals=RepeatedConditionals|true\r
+detectorRuntimeExceptionCapture=RuntimeExceptionCapture|true\r
+detectorSerializableIdiom=SerializableIdiom|true\r
+detectorStartInConstructor=StartInConstructor|true\r
+detectorStaticCalendarDetector=StaticCalendarDetector|true\r
+detectorStringConcatenation=StringConcatenation|true\r
+detectorSuperfluousInstanceOf=SuperfluousInstanceOf|true\r
+detectorSuspiciousThreadInterrupted=SuspiciousThreadInterrupted|true\r
+detectorSwitchFallthrough=SwitchFallthrough|true\r
+detectorSynchronizeAndNullCheckField=SynchronizeAndNullCheckField|true\r
+detectorSynchronizeOnClassLiteralNotGetClass=SynchronizeOnClassLiteralNotGetClass|true\r
+detectorSynchronizingOnContentsOfFieldToProtectField=SynchronizingOnContentsOfFieldToProtectField|true\r
+detectorURLProblems=URLProblems|true\r
+detectorUncallableMethodOfAnonymousClass=UncallableMethodOfAnonymousClass|true\r
+detectorUnnecessaryMath=UnnecessaryMath|true\r
+detectorUnreadFields=UnreadFields|true\r
+detectorUseObjectEquals=UseObjectEquals|false\r
+detectorUselessSubclassMethod=UselessSubclassMethod|false\r
+detectorVarArgsProblems=VarArgsProblems|true\r
+detectorVolatileUsage=VolatileUsage|true\r
+detectorWaitInLoop=WaitInLoop|true\r
+detectorWrongMapIterator=WrongMapIterator|true\r
+detectorXMLFactoryBypass=XMLFactoryBypass|true\r
+detector_threshold=2\r
+effort=default\r
+filter_settings=Medium|BAD_PRACTICE,CORRECTNESS,MT_CORRECTNESS,PERFORMANCE,STYLE|false\r
+filter_settings_neg=MALICIOUS_CODE,NOISE,I18N,SECURITY,EXPERIMENTAL|\r
+run_at_full_build=false\r
本ソフトウェアの使用に起因する一切の直接損害、間接損害、偶発的損害、特別損害、懲戒的損害、派生的損害について作者(seraphy)および再配布者、共同開発者は一切の責任を負わないものとします。</p>\r
<p>このアプリケーションのプロジェクトは、SourceForgeにてオープンソースとして管理しております。</p>\r
<p><a href="http://sourceforge.jp/projects/charactermanaj/">http://sourceforge.jp/projects/charactermanaj/</a>より最新バージョンおよびソースコードを取得することができます。</p>\r
-<p><a href="http://www.apache.org/licenses/LICENSE-2.0">The Apache License Version 2.0</a></b>に従い、だれでも自由に利用でき、且つ、改変することができます。</p>\r
+<p><b><a href="http://www.apache.org/licenses/LICENSE-2.0">The Apache License Version 2.0</a></b>に従い、だれでも自由に利用でき、且つ、改変することができます。</p>\r
<p>このアプリケーション自身(javaコード)は<a href="http://hp.vector.co.jp/authors/VA017626/">seraphy</a>によって書かれました。<br>\r
人的リソースに限りがあるため、メンテナンスや改善に時間がかかることが予想されます。<br>\r
このアプリケーションをより良くしようという有志がおられましたら、ぜひ、ご協力・ご連絡くださいますようお願いいたします。歓迎いたします。</p>\r
本ソフトウェアの使用に起因する一切の直接損害、間接損害、偶発的損害、特別損害、懲戒的損害、派生的損害について作者(seraphy)および再配布者、共同開発者は一切の責任を負わないものとします。</p>\r
<p>このアプリケーションのプロジェクトは、SourceForgeにてオープンソースとして管理しております。</p>\r
<p><a href="http://sourceforge.jp/projects/charactermanaj/">http://sourceforge.jp/projects/charactermanaj/</a>より最新バージョンおよびソースコードを取得することができます。</p>\r
-<p><a href="http://www.apache.org/licenses/LICENSE-2.0">The Apache License Version 2.0</a></b>に従い、だれでも自由に利用でき、且つ、改変することができます。</p>\r
+<p><b><a href="http://www.apache.org/licenses/LICENSE-2.0">The Apache License Version 2.0</a></b>に従い、だれでも自由に利用でき、且つ、改変することができます。</p>\r
<p>このアプリケーション自身(javaコード)は<a href="http://hp.vector.co.jp/authors/VA017626/">seraphy</a>によって書かれました。<br>\r
人的リソースに限りがあるため、メンテナンスや改善に時間がかかることが予想されます。<br>\r
このアプリケーションをより良くしようという有志がおられましたら、ぜひ、ご協力・ご連絡くださいますようお願いいたします。歓迎いたします。</p>\r
<entry key="table.caption">Settings</entry>\r
\r
<entry key="compressionQuality">01;Compression Quality</entry>\r
-<entry key="zipNameEncoding">02;Zip File Encoding</entry>\r
+<entry key="zipNameEncoding">02;ZIP File Encoding</entry>\r
<entry key="commonCharacterDataDir">03;Common Character Data Directory</entry>\r
+<entry key="partsColorGroupPattern">04;The judgment pattern of a color group.('@' is color group name)</entry>\r
\r
<entry key="mainFrameMaxWidth">10;Preview Max-Width</entry>\r
<entry key="mainFrameMaxHeight">11;Preview Max-Height</entry>\r
<entry key="compressionQuality">01;JPEG圧縮時のクオリティ(1が最大、0.1が最小)</entry>\r
<entry key="zipNameEncoding">02;ZIPファイルに格納されているファイル名のエンコーディング(csWindows31Jが標準)</entry>\r
<entry key="commonCharacterDataDir">03;全ユーザー用キャラクター定義ディレクトリ(空はデフォルト使用)</entry>\r
+<entry key="partsColorGroupPattern">04;パーツ名からカラーグループを判定するパターン(正規表現)(@がカラーグループ名の場所になります.)</entry>\r
\r
<entry key="mainFrameMaxWidth">10;プレビューの初期表示の最大幅</entry>\r
<entry key="mainFrameMaxHeight">11;プレビューの初期表示の最大高さ</entry>\r
Method mtd = clz.getMethod("setupScreenMenu", MainFrame.class);\r
mtd.invoke(null, mainFrame);\r
}\r
-\r
- // メインウィンドウを表示.\r
- // (Mainメソッドは、このあと終了するが、Swingはアクティブなウィンドウがいるかぎりアプリケーションを終了しない.)\r
- mainFrame.setVisible(true);\r
+ \r
+ // 表示(および位置あわせ)\r
+ mainFrame.showMainFrame();\r
\r
} catch (Throwable ex) {\r
// なんらかの致命的な初期化エラーがあった場合、ログとコンソールに表示\r
logger.log(Level.SEVERE, "Application initiation failed.", ex);\r
}\r
ErrorMessageHelper.showErrorDialog(null, ex);\r
- System.exit(1);\r
+\r
+ // メインフレームを破棄します.\r
+ MainFrame.closeAllProfiles();\r
}\r
}\r
\r
import java.util.Set;\r
import java.util.logging.Level;\r
import java.util.logging.Logger;\r
+import java.util.regex.Pattern;\r
\r
import charactermanaj.util.ApplicationLogHandler;\r
import charactermanaj.util.BeanPropertiesUtilities;\r
// 無視する\r
}\r
}\r
- } catch (Exception ex) {\r
+\r
+ } catch (IOException ex) {\r
+ throw new RuntimeException("appConfig.xml loading failed.", ex);\r
+ } catch (RuntimeException ex) {\r
throw new RuntimeException("appConfig.xml loading failed.", ex);\r
}\r
BeanPropertiesUtilities.loadFromProperties(this, config);\r
\r
private long purgeLogDays = 10;\r
\r
+ public String getPartsColorGroupPattern() {\r
+ return partsColorGroupPattern;\r
+ }\r
+ \r
+ public void setPartsColorGroupPattern(String pattern) {\r
+ if (pattern != null && pattern.trim().length() > 0) {\r
+ Pattern.compile(pattern);\r
+ }\r
+ partsColorGroupPattern = pattern;\r
+ }\r
+ \r
+ private String partsColorGroupPattern = "^.*\\(@\\).*$";\r
}\r
}\r
\r
/**\r
- * キャラクターデータが同じ構造であるか?\r
- * ã\82µã\82¤ã\82ºã\80\81ã\82«ã\83©ã\83¼ã\82°ã\83«ã\83¼ã\83\97ã\80\81ã\82«ã\83\86ã\82´ã\83ªã\80\81ã\83¬ã\82¤ã\83¤ã\83¼ã\81®å\90\84æ\83\85å ±ã\81\8cç\89ã\81\97ã\81\91ã\82\8cã\81°trueã\80\81ã\81\9dã\82\8c以å¤\96ã\81¯false.<br>\r
+ * キャラクターデータが同じ構造であるか?<br>\r
+ * カラーグループ、カテゴリ、レイヤーの各情報が等しければtrue、それ以外はfalse.<br>\r
* 上記以外の項目(コメントや作者、プリセット等)については判定しない.<br>\r
- * カラーグループの表示名や順序、カテゴリの順序や表示名、複数アイテム可などの違いは構造の変更とみなさない.<br>\r
+ * サイズ、カラーグループの表示名や順序、カテゴリの順序や表示名、\r
+ * 複数アイテム可などの違いは構造の変更とみなさない.<br>\r
* レイヤーはレイヤーID、重ね合わせ順、対象ディレクトリの3点が変更されている場合は構造の変更とみなす.<br>\r
* いずれも個数そのものが変わっている場合は変更とみなす.<br>\r
* 自分または相手がValidでなければ常にfalseを返す.<br>\r
- * @param other 比較対象\r
+ * @param other 比較対象, null可\r
* @return 同じ構造であればtrue、そうでなければfalse\r
*/\r
public boolean isSameStructure(CharacterData other) {\r
- if (other == null) {\r
- throw new IllegalArgumentException();\r
- }\r
- if (!this.isValid() || !other.isValid()) {\r
+ if (!this.isValid() || other == null || !other.isValid()) {\r
// 自分または相手がinvalidであれば構造的には常に不一致と見なす.\r
return false;\r
}\r
\r
- // サイズが等しいか?\r
- if (imageSize == null) {\r
- if (other.images != null) {\r
- return false;\r
- }\r
- } else if (other.images == null || !imageSize.equals(other.imageSize)) {\r
- return false;\r
- }\r
- \r
// カラーグループが等しいか? (順序は問わない)\r
// IDのみによって判定する\r
ArrayList<ColorGroup> colorGroup1 = new ArrayList<ColorGroup>(getColorGroups());\r
return ret;\r
}\r
};\r
- // カテゴリID順に並び替えて比較する.\r
+ // カテゴリID順に並び替えて, IDのみを比較する.\r
Collections.sort(categories1, sortCategoryId);\r
Collections.sort(categories2, sortCategoryId);\r
- if (!categories1.equals(categories2)) {\r
+ int numOfCategories = categories1.size();\r
+ if (numOfCategories != categories2.size()) {\r
+ // カテゴリ数不一致\r
return false;\r
}\r
+ for (int idx = 0; idx < numOfCategories; idx++) {\r
+ PartsCategory category1 = categories1.get(idx);\r
+ PartsCategory category2 = categories2.get(idx);\r
+ String categoryId1 = category1.getCategoryId();\r
+ String categoryId2 = category2.getCategoryId();\r
+ if ( !categoryId1.equals(categoryId2)) {\r
+ // カテゴリID不一致\r
+ return false;\r
+ }\r
+ }\r
\r
// レイヤーが等しいか?\r
// ID、重ね順序、dirによってのみ判定する.\r
Collections.sort(layers1, sortLayerId);\r
Collections.sort(layers2, sortLayerId);\r
\r
- if (!layers1.equals(layers2)) {\r
+ // ID、順序、Dirで判断する.(それ以外のレイヤー情報はequalsでは比較されない)\r
+ if ( !layers1.equals(layers2)) {\r
+ // レイヤー不一致\r
return false;\r
}\r
}\r
* 構造が同一であるか、サイズ違い、もしくはレイヤーの順序、カテゴリの順序、\r
* もしくはレイヤーまたはカテゴリが増えている場合で、減っていない場合はtrueとなる.<br>\r
* 引数がnullの場合は常にfalseとなる.\r
- * @param previous 前の状態のキャラクター定義、null可\r
+ * @param other 前の状態のキャラクター定義、null可\r
* @return アッパーコンパチブルであればtrue、そうでなければfalse\r
*/\r
- public boolean isUpperCompatibleStructure(CharacterData previous) {\r
- return isSameStructure(previous); // TODO: 実装する。いまは仮\r
+ public boolean isUpperCompatibleStructure(CharacterData other) {\r
+ if (!this.isValid() || other == null || !other.isValid()) {\r
+ // 自分または相手がinvalidであれば構造的には常に互換性なしと見なす.\r
+ return false;\r
+ }\r
+\r
+ // カラーグループが等しいか? (順序は問わない)\r
+ // IDのみによって判定する\r
+ ArrayList<ColorGroup> colorGroupNew = new ArrayList<ColorGroup>(getColorGroups());\r
+ ArrayList<ColorGroup> colorGroupOld = new ArrayList<ColorGroup>(other.getColorGroups());\r
+ if (!colorGroupNew.containsAll(colorGroupOld)) {\r
+ // 自分が相手分のすべてのものを持っていなければ互換性なし.\r
+ return false;\r
+ }\r
+ \r
+ // カテゴリをすべて含むか? (順序は問わない)\r
+ // IDによってのみ判定する.\r
+ Map<String, PartsCategory> categoriesNew = new HashMap<String, PartsCategory>();\r
+ for (PartsCategory category : getPartsCategories()) {\r
+ categoriesNew.put(category.getCategoryId(), category);\r
+ }\r
+ Map<String, PartsCategory> categoriesOld = new HashMap<String, PartsCategory>();\r
+ for (PartsCategory category : other.getPartsCategories()) {\r
+ categoriesOld.put(category.getCategoryId(), category);\r
+ }\r
+ if ( !categoriesNew.keySet().containsAll(categoriesOld.keySet())) {\r
+ // 自分が相手のすべてのカテゴリを持っていなければ互換性なし.\r
+ return false;\r
+ }\r
+ \r
+ // レイヤーをすべて含むか?\r
+ // ID、Dirによってのみ判定する.\r
+ for (Map.Entry<String, PartsCategory> categoryOldEntry : categoriesOld.entrySet()) {\r
+ String categoryId = categoryOldEntry.getKey();\r
+ PartsCategory categoryOld = categoryOldEntry.getValue();\r
+ PartsCategory categoryNew = categoriesNew.get(categoryId);\r
+ if (categoryNew == null) {\r
+ return false;\r
+ }\r
+ \r
+ Map<String, Layer> layersNew = new HashMap<String, Layer>();\r
+ for (Layer layer : categoryNew.getLayers()) {\r
+ layersNew.put(layer.getId(), layer);\r
+ }\r
+ Map<String, Layer> layersOld = new HashMap<String, Layer>();\r
+ for (Layer layer : categoryOld.getLayers()) {\r
+ layersOld.put(layer.getId(), layer);\r
+ }\r
+ \r
+ if ( !layersNew.keySet().containsAll(layersOld.keySet())) {\r
+ // 自分が相手のすべてのレイヤー(ID)を持っていなければ互換性なし.\r
+ return false;\r
+ }\r
+ for (Map.Entry<String, Layer> layerOldEntry : layersOld.entrySet()) {\r
+ String layerId = layerOldEntry.getKey();\r
+ Layer layerOld = layerOldEntry.getValue();\r
+ Layer layerNew = layersNew.get(layerId);\r
+ if (layerNew == null) {\r
+ return false;\r
+ }\r
+ String dirOld = layerOld.getDir();\r
+ String dirNew = layerNew.getDir();\r
+ if ( !dirOld.equals(dirNew)) {\r
+ // ディレクトリが一致しなければ互換性なし.\r
+ // (XXX: 大文字・小文字も厳格に区別している. Windows環境はどうするべきか?)\r
+ return false;\r
+ }\r
+ }\r
+ }\r
+ \r
+ return true;\r
}\r
\r
/**\r
import java.util.HashMap;\r
import java.util.Map;\r
import java.util.Set;\r
+import java.util.logging.Level;\r
+import java.util.logging.Logger;\r
\r
import charactermanaj.graphics.filters.ColorConvertParameter;\r
\r
*/\r
public final class PartsColorInfo extends AbstractMap<Layer, ColorInfo> implements Serializable, Cloneable {\r
\r
+ /**\r
+ * ロガー\r
+ */\r
+ private static final Logger logger = Logger.getLogger(PartsColorInfo.class.getName());\r
+ \r
private static final long serialVersionUID = -8639109147043912257L;\r
\r
/**\r
init();\r
}\r
\r
- protected PartsColorInfo(PartsColorInfo src) {\r
- if (src == null) {\r
+ @Override\r
+ public PartsColorInfo clone() {\r
+ try {\r
+ PartsColorInfo inst = (PartsColorInfo) super.clone();\r
+ inst.colorInfoMap = new HashMap<Layer, ColorInfo>();\r
+ for (Map.Entry<Layer, ColorInfo> entry : colorInfoMap.entrySet()) {\r
+ Layer layer = entry.getKey();\r
+ ColorInfo colorInfo = entry.getValue();\r
+ inst.colorInfoMap.put(layer, colorInfo.clone());\r
+ }\r
+ return inst;\r
+\r
+ } catch (CloneNotSupportedException ex) {\r
+ throw new RuntimeException(ex);\r
+ }\r
+ }\r
+ \r
+ /**\r
+ * パーツカラー情報を指定したパーツカテゴリに存在するレイヤーに正規化して返す.<br>\r
+ * カテゴリに存在しないレイヤーの情報は破棄され、結果は有効なレイヤーのみの色情報となる.<br>\r
+ * @param partsCategory パーツカテゴリ\r
+ * @return 正規化されたパーツカラー情報\r
+ */\r
+ public PartsColorInfo createCompatible(PartsCategory partsCategory) {\r
+ if (partsCategory == null) {\r
throw new IllegalArgumentException();\r
}\r
- this.partsCategory = src.partsCategory;\r
- init();\r
- for (Map.Entry<Layer, ColorInfo> entry : src.colorInfoMap.entrySet()) {\r
+ PartsColorInfo newInfo = new PartsColorInfo(partsCategory);\r
+ newInfo.init();\r
+ for (Map.Entry<Layer, ColorInfo> entry : colorInfoMap.entrySet()) {\r
Layer layer = entry.getKey();\r
ColorInfo colorInfo = entry.getValue();\r
- colorInfoMap.put(layer, colorInfo.clone());\r
+ if (partsCategory.hasLayer(layer)) {\r
+ newInfo.put(layer, colorInfo.clone());\r
+ } else {\r
+ logger.log(Level.INFO, "missing layer '" + layer + "' in " + partsCategory);\r
+ }\r
}\r
- }\r
- \r
- @Override\r
- public PartsColorInfo clone() {\r
- return new PartsColorInfo(this);\r
+ return newInfo;\r
}\r
\r
/**\r
private static final Logger logger = Logger.getLogger(PartsColorManager.class.getName());\r
\r
/**\r
+ * カテゴリごとのパーツカラー情報.<br>\r
+ * @author seraphy\r
+ */\r
+ public static final class CategoryColorInfo {\r
+ \r
+ private final PartsColorInfo partsColorInfo;\r
+ \r
+ private final boolean applyAll;\r
+ \r
+ public CategoryColorInfo(PartsColorInfo partsColorInfo, boolean applyAll) {\r
+ this.partsColorInfo = partsColorInfo;\r
+ this.applyAll = applyAll;\r
+ }\r
+ \r
+ public PartsColorInfo getPartsColorInfo() {\r
+ return partsColorInfo;\r
+ }\r
+ \r
+ public boolean isApplyAll() {\r
+ return applyAll;\r
+ }\r
+ }\r
+ \r
+ /**\r
* パーツ単位でのカラーグループを含む色情報.<br>\r
* カテゴリ全体に適用される場合はパーツ単位の色情報はリセットする.<br>\r
*/\r
- private HashMap<PartsIdentifier, PartsColorInfo> partsColorInfoMap = new HashMap<PartsIdentifier, PartsColorInfo>();\r
+ private HashMap<PartsIdentifier, PartsColorInfo> partsColorInfoMap\r
+ = new HashMap<PartsIdentifier, PartsColorInfo>();\r
\r
/**\r
* カテゴリごとに共通となる場合のカラーグループを含む色情報.<br>\r
* パーツ単位の色情報が定義されていない場合、カテゴリ単位での情報が使用される.<br>\r
*/\r
- private HashMap<PartsCategory, PartsColorInfo> categoryColorInfoMap = new HashMap<PartsCategory, PartsColorInfo>();\r
+ private HashMap<PartsCategory, CategoryColorInfo> categoryColorInfoMap\r
+ = new HashMap<PartsCategory, CategoryColorInfo>();\r
\r
/**\r
* カラーグループごとの色情報.<br>\r
*/\r
- private HashMap<ColorGroup, ColorConvertParameter> recentColorGroupMap = new HashMap<ColorGroup, ColorConvertParameter>();\r
+ private HashMap<ColorGroup, ColorConvertParameter> recentColorGroupMap\r
+ = new HashMap<ColorGroup, ColorConvertParameter>();\r
\r
+ /**\r
+ * パーツ設定のリゾルバ\r
+ */\r
private PartsSpecResolver partsSpecResolver;\r
\r
+ /**\r
+ * パーツ設定リゾルバを指定して構築する.\r
+ * @param partsSpecResolver リゾルバ\r
+ */\r
public PartsColorManager(PartsSpecResolver partsSpecResolver) {\r
if (partsSpecResolver == null) {\r
throw new IllegalArgumentException();\r
PartsCategory partsCategory = partsIdentifier.getPartsCategory();\r
\r
if (applyAll) {\r
- // カテゴリ指定\r
- resetPartsColorInfo(partsCategory); // パーツ個別色をリセットすることでカテゴリを優先させる.\r
- \r
+ // カテゴリ指定の場合\r
+ // パーツ個別色をリセットすることでカテゴリを優先させる.\r
+ resetPartsColorInfo(partsCategory);\r
+\r
if (logger.isLoggable(Level.FINEST)) {\r
logger.log(Level.FINEST, "setPartsColorInfo(Category): " + partsIdentifier + "=" + partsColorInfo);\r
}\r
\r
} else {\r
- // パーツ個別指定\r
+ // パーツ個別指定の場合\r
partsColorInfoMap.put(partsIdentifier, partsColorInfo);\r
\r
if (logger.isLoggable(Level.FINEST)) {\r
}\r
}\r
\r
- setRecentColorGroup(partsColorInfo);\r
- categoryColorInfoMap.put(partsCategory, partsColorInfo);\r
+ // カラーグループとしての最新のカラー情報を保存する.(有効なカラーグループで連動指定がある場合のみ)\r
+ // ただし、「すべてに適用」でない場合は保存しない.\r
+ if (applyAll) {\r
+ setRecentColorGroup(partsColorInfo);\r
+ }\r
+\r
+ // カテゴリごとの最新の色情報を設定する.\r
+ // (「すべてに適用」であるか、単数選択カテゴリで、まだ「すべてに適用」の色情報がない場合のみ.)\r
+ // (複数選択カテゴリの場合は明示的に「すべてに適用」を選択していないかぎり保存されない.)\r
+ CategoryColorInfo categoryColorInfo = categoryColorInfoMap.get(partsCategory);\r
+ if (applyAll ||\r
+ (!partsCategory.isMultipleSelectable() &&\r
+ (categoryColorInfo == null || !categoryColorInfo.isApplyAll()))) {\r
+ categoryColorInfo = new CategoryColorInfo(partsColorInfo, applyAll);\r
+ categoryColorInfoMap.put(partsCategory, categoryColorInfo);\r
+ }\r
}\r
\r
/**\r
* パーツの色情報を指定して、パーツ識別子の各レイーヤの色グループ情報を保存します.<br>\r
+ * 連動指定が有効であり、有効なカラーグループである場合のみ保存されます.<br>\r
* @param partsColorInfo パーツ識別子\r
*/\r
protected void setRecentColorGroup(PartsColorInfo partsColorInfo) {\r
* @param partsCategory パーツカテゴリ\r
* @return 指定したパーツカテゴリの色情報、またはnull\r
*/\r
- public PartsColorInfo getPartsColorInfo(PartsCategory partsCategory) {\r
+ public CategoryColorInfo getPartsColorInfo(PartsCategory partsCategory) {\r
return categoryColorInfoMap.get(partsCategory);\r
}\r
\r
}\r
\r
/**\r
- * 指定したカテゴリに属するパーツ識別子ごとの色情報をリセットします.<br>\r
- * 引数partsCategoryがnullの場合は全パーツ識別子をリセットします.<br>\r
+ * æ\8c\87å®\9aã\81\97ã\81\9fã\82«ã\83\86ã\82´ã\83ªã\81¨ã\80\81ã\82«ã\83\86ã\82´ã\83ªã\81«å±\9eã\81\99ã\82\8bã\83\91ã\83¼ã\83\84è\98å\88¥å\90ã\81\94ã\81¨ã\81®è\89²æ\83\85å ±ã\82\92ã\83ªã\82»ã\83\83ã\83\88ã\81\97ã\81¾ã\81\99.<br>\r
+ * å¼\95æ\95°partsCategoryã\81\8cnullã\81®å ´å\90\88ã\81¯å\85¨ã\83\91ã\83¼ã\83\84è\98å\88¥å\90ã\80\81å\85¨ã\82«ã\83\86ã\82´ã\83ªã\81¨ã\80\81ã\81\99ã\81¹ã\81¦ã\81®ã\82«ã\83©ã\83¼ã\82°ã\83«ã\83¼ã\83\97ã\82\92ã\83ªã\82»ã\83\83ã\83\88ã\81\97ã\81¾ã\81\99.<br>\r
* @param partsCategory パーツカテゴリまたはnull\r
*/\r
public void resetPartsColorInfo(PartsCategory partsCategory) {\r
if (partsCategory == null) {\r
+ recentColorGroupMap.clear();\r
partsColorInfoMap.clear();\r
+ categoryColorInfoMap.clear();\r
return;\r
}\r
\r
+ categoryColorInfoMap.remove(partsCategory);\r
Iterator<Map.Entry<PartsIdentifier, PartsColorInfo>> ite = partsColorInfoMap.entrySet().iterator();\r
while (ite.hasNext()) {\r
Map.Entry<PartsIdentifier, PartsColorInfo> entry = ite.next();\r
PartsCategory category = partsIdentifier.getPartsCategory();\r
PartsColorInfo partsColorInfo = new PartsColorInfo(category);\r
\r
- // 同一カテゴリの最近設定されたカラー情報を適用する.\r
- PartsColorInfo categoryPartsColorInfo = categoryColorInfoMap.get(category);\r
- if (categoryPartsColorInfo != null) {\r
- for (Map.Entry<Layer, ColorInfo> entry : categoryPartsColorInfo.entrySet()) {\r
- Layer layer = entry.getKey();\r
- ColorInfo colorInfo = entry.getValue();\r
- if (colorInfo != null && partsColorInfo.containsKey(layer)) {\r
- partsColorInfo.put(layer, colorInfo.clone());\r
- }\r
- }\r
- }\r
- \r
- // パーツ固有の設定があり、パーツ固定のカラーグループの指定があれば\r
- // 全レイヤーを該当カラーグループに設定する.\r
+ // パーツ固有のカラーグループの指定があるか?\r
PartsSpec partsSpec = partsSpecResolver.getPartsSpec(partsIdentifier);\r
+ ColorGroup partsSpecColorGroup = null;\r
if (partsSpec != null) {\r
- ColorGroup colorGroup = partsSpec.getColorGroup();\r
- if (colorGroup != null && colorGroup.isEnabled()) {\r
- for (Map.Entry<Layer, ColorInfo> entry : partsColorInfo.entrySet()) {\r
+ partsSpecColorGroup = partsSpec.getColorGroup();\r
+ }\r
+\r
+ if (partsSpecColorGroup != null && partsSpecColorGroup.isEnabled()) {\r
+ // パーツ固定のカラーグループの指定があれば\r
+ // 全レイヤーを該当カラーグループに設定する.\r
+ for (Map.Entry<Layer, ColorInfo> entry : partsColorInfo.entrySet()) {\r
+ ColorInfo colorInfo = entry.getValue();\r
+ colorInfo = colorInfo.clone();\r
+ colorInfo.setColorGroup(partsSpecColorGroup);\r
+ colorInfo.setSyncColorGroup(true);\r
+ entry.setValue(colorInfo);\r
+ }\r
+\r
+ } else {\r
+ // パーツ固有のカラーグループがなければ\r
+ // 同一カテゴリの最近設定されたカラー情報をもとに、パーツカラー情報を作成する.\r
+ CategoryColorInfo categoryColorInfo = categoryColorInfoMap.get(category);\r
+ if (categoryColorInfo != null) {\r
+ PartsColorInfo categoryPartsColorInfo = categoryColorInfo.getPartsColorInfo();\r
+ for (Map.Entry<Layer, ColorInfo> entry : categoryPartsColorInfo.entrySet()) {\r
+ Layer layer = entry.getKey();\r
ColorInfo colorInfo = entry.getValue();\r
- if (colorInfo != null) {\r
+ if (colorInfo != null && partsColorInfo.containsKey(layer)) {\r
colorInfo = colorInfo.clone();\r
- } else {\r
- colorInfo = new ColorInfo();\r
+ \r
+ // ただし、同一カテゴリに設定されたカラー情報が「すべてに適用」でない場合は、\r
+ // レイヤー固有のカラーグループを維持する.\r
+ if ( !categoryColorInfo.isApplyAll()) {\r
+ ColorGroup layerColorGroup = layer.getColorGroup();\r
+ if (layerColorGroup == null) {\r
+ layerColorGroup = ColorGroup.NA;\r
+ }\r
+ colorInfo.setColorGroup(layerColorGroup);\r
+ }\r
+\r
+ partsColorInfo.put(layer, colorInfo);\r
}\r
- colorInfo.setColorGroup(colorGroup);\r
- colorInfo.setSyncColorGroup(true);\r
- entry.setValue(colorInfo);\r
}\r
}\r
}\r
this.bgColor = org.bgColor;\r
this.affineTransformParameter = org.affineTransformParameter == null ? null : org.affineTransformParameter.clone();\r
\r
+ // ColorInfoMapの正規化\r
for (Map.Entry<PartsIdentifier, PartsColorInfo> partsColorInfoEntry : org.partsColorInfoMap.entrySet()) {\r
PartsIdentifier partsIdentifier = partsColorInfoEntry.getKey();\r
if (resolver != null) {\r
partsIdentifier.getLocalizedPartsName());\r
}\r
}\r
- \r
- PartsColorInfo copiedPartsColorInfo = partsColorInfoEntry.getValue().clone();\r
+ PartsCategory repPartsCategory = partsIdentifier.getPartsCategory();\r
+ PartsColorInfo copiedPartsColorInfo = partsColorInfoEntry\r
+ .getValue().createCompatible(repPartsCategory);\r
partsColorInfoMap.put(partsIdentifier, copiedPartsColorInfo);\r
}\r
\r
+ // PartsIdentifierの正規化\r
for (Map.Entry<PartsCategory, List<PartsIdentifier>> partsEntry : org.parts.entrySet()) {\r
PartsCategory orgPartsCategory = partsEntry.getKey();\r
PartsCategory partsCategory = orgPartsCategory;\r
if (resolver == null) {\r
throw new IllegalArgumentException();\r
}\r
- // FIXME: 本当はこんなことはしたくない。\r
+ // XXX: 本当は、こんなことはしたくない。\r
return new PartsSet(this, resolver);\r
}\r
\r
*/\r
public boolean isSameStructure(PartsSet other) {\r
if (other != null && other.size() == this.size()) {\r
- for (PartsCategory category : keySet()) {\r
- List<PartsIdentifier> ownList = get(category);\r
+ // カテゴリが一致し、各カテゴリに登録されているパーツ識別子のリストも順序を含めて一致する場合、\r
+ // 構造的に一致すると判定する.\r
+ for (Map.Entry<PartsCategory, List<PartsIdentifier>> entry : entrySet()) {\r
+ PartsCategory category = entry.getKey();\r
+ List<PartsIdentifier> ownList = entry.getValue();\r
List<PartsIdentifier> otherList = other.get(category);\r
if (ownList == null || otherList == null || !ownList.equals(otherList)) {\r
return false;\r
}\r
}\r
- // カテゴリが一致し、各カテゴリに登録されているパーツ識別子のリストも順序を含めて一致する場合、\r
- // 構造的に一致すると判定する.\r
return true;\r
}\r
return false;\r
package charactermanaj.model.io;\r
\r
import java.awt.Color;\r
+import java.awt.Dimension;\r
import java.awt.image.BufferedImage;\r
import java.io.File;\r
import java.io.IOException;\r
import java.io.InputStream;\r
import java.io.OutputStream;\r
import java.io.OutputStreamWriter;\r
+import java.io.Writer;\r
import java.nio.charset.Charset;\r
+import java.sql.Timestamp;\r
+import java.util.HashMap;\r
+import java.util.List;\r
import java.util.Map;\r
import java.util.Properties;\r
\r
import charactermanaj.model.AppConfig;\r
import charactermanaj.model.CharacterData;\r
import charactermanaj.model.Layer;\r
+import charactermanaj.model.PartsCategory;\r
import charactermanaj.model.PartsFiles;\r
import charactermanaj.model.PartsIdentifier;\r
import charactermanaj.model.PartsManageData;\r
import charactermanaj.model.PartsManageDataConverter;\r
+import charactermanaj.model.PartsSet;\r
import charactermanaj.model.PartsSpec;\r
\r
public abstract class AbstractCharacterDataArchivedFileWriter extends AbstractCharacterDataFileWriter {\r
throws IOException {\r
CharacterDataPersistent persist = CharacterDataPersistent.getInstance();\r
\r
- // character data\r
+ // character.xmlの出力\r
putNextEntry("character.xml", 0);\r
persist.writeXMLCharacterData(characterData, getOutputStream());\r
closeEntry();\r
+ \r
+ // character.iniの出力\r
+ internalWriteCharacterIni(characterData);\r
+ }\r
+\r
+ /**\r
+ * character.iniを出力します.<br>\r
+ * @param characterData キャラクターデータ\r
+ * @throws IOException 出力に失敗した場合\r
+ */\r
+ protected void internalWriteCharacterIni(CharacterData characterData) throws IOException {\r
+ StringBuilder buf = new StringBuilder();\r
+\r
+ buf.append("; created by charactermanaj "\r
+ + new Timestamp(System.currentTimeMillis()) + "\r\n");\r
+ \r
+ buf.append("[Size]\r\n");\r
+ Dimension dim = characterData.getImageSize();\r
+ if (dim == null) {\r
+ dim = new Dimension(300, 400);\r
+ }\r
+ buf.append("size_x=" + dim.width + "\r\n");\r
+ buf.append("size_y=" + dim.height + "\r\n");\r
+\r
+ buf.append("\r\n");\r
+ buf.append("[Parts]\r\n");\r
+ \r
+ Map<String, String> partsMap = new HashMap<String, String>();\r
+ for (PartsCategory partsCategory : characterData.getPartsCategories()) {\r
+ String categoryId = partsCategory.getCategoryId();\r
+ partsMap.put(categoryId, "");\r
+ }\r
+ \r
+ Map<String, PartsSet> partsSets = characterData.getPartsSets();\r
+ PartsSet partsSet = partsSets.get(characterData.getDefaultPartsSetId());\r
+ if (partsSet == null && !partsSets.isEmpty()) {\r
+ // デフォルトのパーツセットが指定されていない場合は、どれか1つを選択する.\r
+ partsSet = partsSets.values().iterator().next();\r
+ }\r
+ if (partsSet != null) {\r
+ for (Map.Entry<PartsCategory, List<PartsIdentifier>> entry : partsSet\r
+ .entrySet()) {\r
+ PartsCategory partsCategory = entry.getKey();\r
+ StringBuilder partsNames = new StringBuilder();\r
+ for (PartsIdentifier partsIdentifier : entry.getValue()) {\r
+ if (partsNames.length() > 0) {\r
+ partsNames.append(",");\r
+ }\r
+ partsNames.append(partsIdentifier.getPartsName());\r
+ }\r
+ String categoryId = partsCategory.getCategoryId();\r
+ partsMap.put(categoryId, partsNames.toString());\r
+ }\r
+ }\r
+ for (PartsCategory partsCategory : characterData.getPartsCategories()) {\r
+ String categoryId = partsCategory.getCategoryId();\r
+ String partsNames = partsMap.get(categoryId);\r
+ buf.append(categoryId + "=" + partsNames + "\r\n");\r
+ }\r
+\r
+ // 色情報はすべてダミー(character.iniは色情報を省略しても問題ないようだが、一応)\r
+ buf.append("\r\n");\r
+ buf.append("[Color]\r\n");\r
+ buf.append("hair_rgb=0\r\n");\r
+ buf.append("hair_gray=0\r\n");\r
+ buf.append("eye_rgb=0\r\n");\r
+ buf.append("eye_gray=0\r\n");\r
+ buf.append("skin_rgb=0\r\n");\r
+ buf.append("skin_gray=0\r\n");\r
+ buf.append("body_rgb=0\r\n");\r
+ buf.append("body_gray=0\r\n");\r
+\r
+ // UTF16LEで出力する.\r
+ internalWriteTextUTF16LE("character.ini", buf.toString());\r
}\r
\r
@Override\r
os.write((byte) 0xff);\r
os.write((byte) 0xfe);\r
os.flush();\r
- OutputStreamWriter wr = new OutputStreamWriter(os, Charset.forName("UTF-16LE"));\r
+ Writer wr = new OutputStreamWriter(os, Charset.forName("UTF-16LE")) {\r
+ @Override\r
+ public void close() throws IOException {\r
+ // ZipのOutputStreamをクローズしてはならないため\r
+ // OutputStreamWriter自身はクローズは呼び出さない.\r
+ flush();\r
+ closeEntry();\r
+ }\r
+ };\r
try {\r
wr.append(contents);\r
wr.flush();\r
} finally {\r
- closeEntry();\r
+ wr.close();\r
}\r
}\r
\r
\r
String EXPORT_CHARACTER_DATA = "EXPORT_CHARACTER_DATA";\r
\r
- String EXPORT_SUBSET = "EXPORT_SUBSET";\r
- \r
String EXPORT_PARTS_IMAGES = "EXPORT_PARTS_IMAGES";\r
\r
String EXPORT_TIMESTAMP = "EXPORT_TIMESTAMP";\r
import java.util.Collection;\r
import java.util.Collections;\r
import java.util.Map;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
\r
+import charactermanaj.model.AppConfig;\r
import charactermanaj.model.ColorGroup;\r
import charactermanaj.model.PartsCategory;\r
import charactermanaj.model.PartsIdentifier;\r
}\r
\r
/**\r
- * ã\83\91ã\83¼ã\83\84è\98å\88¥å\90ã\81®è¡¨ç¤ºå\90\8dã\81®æ\9c«å°¾ã\81\8cã\82«ã\83©ã\83¼ã\82°ã\83«ã\83¼ã\83\97ã\81®è¡¨ç¤ºå\90\8dã\82\92æ\8b¬å¼§ã\81§ã\81\8fã\81\8fã\81£ã\81\9fã\82\82ã\81®ã\81¨ç\89ã\81\97ã\81\84場合、\r
+ * ã\83\91ã\83¼ã\83\84è\98å\88¥å\90ã\81®è¡¨ç¤ºå\90\8dã\81«ã\80\81ã\82«ã\83©ã\83¼ã\82°ã\83«ã\83¼ã\83\97ã\81®è¡¨ç¤ºå\90\8dã\81«ã\82\88ã\82\8aå\88¤å®\9aã\81\95ã\82\8cã\82\8bã\83\91ã\82¿ã\83¼ã\83³ã\81«å\90\88è\87´ã\81\99ã\82\8b場合、\r
* パーツ設定のカラーグループを、そのカラーグループとして設定する.\r
* @param partsSpecs パーツマップ\r
*/\r
protected void decolatePartsSpec(Map<PartsIdentifier, PartsSpec> partsSpecs) {\r
+ String templ = AppConfig.getInstance().getPartsColorGroupPattern();\r
+ if (templ == null || templ.trim().length() == 0) {\r
+ // パターンが設定されていない場合は無視する.\r
+ return;\r
+ }\r
// パーツ名にカラーグループが含まれる場合、それを登録する.\r
for (ColorGroup colorGroup : colorGroups) {\r
- String suffix = "(" + colorGroup.getLocalizedName() + ")";\r
+ String pattern = templ.replace("@", colorGroup.getLocalizedName());\r
+ Pattern pat = Pattern.compile(pattern);\r
for (PartsSpec partsSpec : partsSpecs.values()) {\r
- if (partsSpec.getPartsIdentifier().getLocalizedPartsName().endsWith(suffix)) {\r
+ Matcher mat = pat.matcher(partsSpec.getPartsIdentifier()\r
+ .getLocalizedPartsName());\r
+ if (mat.matches()) {\r
partsSpec.setColorGroup(colorGroup);\r
}\r
}\r
\r
import java.io.File;\r
import java.net.URI;\r
+import java.net.URL;\r
import java.util.HashMap;\r
import java.util.Map;\r
+import java.util.UUID;\r
import java.util.logging.Level;\r
import java.util.logging.Logger;\r
\r
new PurgeUnusedCache(),\r
};\r
for (StartupSupport startup : startups) {\r
- logger.log(Level.FINE, "startup operation start.");\r
+ logger.log(Level.FINE, "startup operation start. class="\r
+ + startup.getClass().getSimpleName());\r
try {\r
startup.doStartup();\r
+ logger.log(Level.INFO, "startup operation is done.");\r
+\r
} catch (Exception ex) {\r
logger.log(Level.WARNING, "startup operation failed.", ex);\r
}\r
- logger.log(Level.INFO, "startup operation is done.");\r
}\r
}\r
};\r
abstract class StartupSupportForDocBasedData extends StartupSupport {\r
\r
/**\r
+ * character.xmlのファイル位置を示すUUID表現を算定するためのアルゴリズムの選択肢.<br>\r
+ * @author seraphy\r
+ */\r
+ protected enum DocBaseSignatureStoratage {\r
+ \r
+ /**\r
+ * 新形式のcharacter.xmlのUUIDを取得する.\r
+ * charatcer.xmlファイルのURIを文字列にしたもののタイプ3-UUID表現.<br>\r
+ */\r
+ NEW_FORMAT() {\r
+ @Override\r
+ public String getDocBaseSignature(File characterXmlFile) {\r
+ URI docBase = characterXmlFile.toURI();\r
+ UserDataFactory userDataFactory = UserDataFactory.getInstance();\r
+ return userDataFactory.getMangledNamedPrefix(docBase);\r
+ }\r
+ },\r
+ \r
+ /**\r
+ * 旧形式のcharacter.xmlのUUIDを取得する.<br>\r
+ * charatcer.xmlファイルのURLを文字列にしたもののタイプ3-UUID表現.<br>\r
+ */\r
+ OLD_FORMAT() {\r
+ @Override\r
+ public String getDocBaseSignature(File characterXmlFile) {\r
+ try {\r
+ @SuppressWarnings("deprecation")\r
+ URL url = characterXmlFile.toURL();\r
+ return UUID.nameUUIDFromBytes(url.toString().getBytes()).toString();\r
+\r
+ } catch (Exception ex) {\r
+ logger.log(Level.WARNING,\r
+ "character.xmlのファイル位置をUUID化できません。:"\r
+ + characterXmlFile, ex);\r
+ return null;\r
+ }\r
+ }\r
+ },\r
+ ;\r
+ \r
+ /**\r
+ * ロガー\r
+ */\r
+ private static Logger logger = Logger.getLogger(\r
+ DocBaseSignatureStoratage.class.getName());\r
+ \r
+ /**\r
+ * character.xmlからuuid表現のプレフィックスを算定する.\r
+ * @param characterXmlFile character.xmlのファイル\r
+ * @return UUID\r
+ */\r
+ public abstract String getDocBaseSignature(File characterXmlFile);\r
+ }\r
+ \r
+ /**\r
* すべてのユーザおよびシステムのキャラクターデータのDocBaseをもととしたハッシュ値(Prefix)の文字列をキーとし、\r
* キャラクターディレクトリを値とするマップを返す.<br>\r
* @return DocBaseをもととしたハッシュ値の文字列表記をキー、キャラクターディレクトリを値とするマップ\r
*/\r
- protected Map<String, File> getDocBaseMap() {\r
- UserDataFactory userDataFactory = UserDataFactory.getInstance();\r
+ protected Map<String, File> getDocBaseMap(DocBaseSignatureStoratage storatage) {\r
+ if (storatage == null) {\r
+ throw new IllegalArgumentException();\r
+ }\r
AppConfig appConfig = AppConfig.getInstance();\r
\r
File[] charactersDirs = {\r
if ( !characterXml.exists()) {\r
continue;\r
}\r
- URI docBase = characterXml.toURI();\r
- String docBaseSig = userDataFactory.getMangledNamedPrefix(docBase);\r
- docBaseSignatures.put(docBaseSig, characterDir);\r
+ String docBaseSig = storatage.getDocBaseSignature(characterXml);\r
+ if (docBaseSig != null) {\r
+ docBaseSignatures.put(docBaseSig, characterDir);\r
+ }\r
}\r
}\r
return docBaseSignatures;\r
File appData = userDataFactory.getSpecialDataDir(null);\r
\r
// キャラクターデータディレクトリを走査しdocBaseの識別子の一覧を取得する\r
- Map<String, File> docBaseSignatures = getDocBaseMap();\r
+ Map<String, File> docBaseSignatures = getDocBaseMap(\r
+ DocBaseSignatureStoratage.OLD_FORMAT);\r
\r
// ver0.94までは*.favorite.xmlはユーザディレクトリ直下に配備していたが\r
- // ver0.95以降は各キャラクターディレクトリに移動するため、旧docbase-favorites.xmlが残っていれば移動する\r
+ // ver0.95以降は各キャラクターディレクトリに移動するため、旧docbase-id-favorites.xmlが残っていれば移動する\r
// ユーザディレクトリ直下にある*-facotites.xmlを列挙する\r
Map<String, File> favorites = getUUIDMangledNamedMap(appData, "-favorites.xml");\r
\r
@Override\r
public void doStartup() {\r
// キャラクターデータディレクトリを走査しdocBaseの識別子の一覧を取得する\r
- Map<String, File> docBaseSignatures = getDocBaseMap();\r
+ Map<String, File> docBaseSignatures = getDocBaseMap(\r
+ DocBaseSignatureStoratage.NEW_FORMAT);\r
\r
// キャッシュの保存先を取得する.\r
UserDataFactory userDataFactory = UserDataFactory.getInstance();\r
File cacheFile = cacheEntry.getValue();\r
try {\r
if ( !docBaseSignatures.containsKey(mangledUUID)) {\r
- cacheFile.delete();\r
- logger.log(Level.INFO, "purge unused cache: " + cacheFile);\r
+ boolean result = cacheFile.delete();\r
+ logger.log(Level.INFO, "purge unused cache: " + cacheFile\r
+ + "/succeeded=" + result);\r
}\r
\r
} catch (Exception ex) {\r
if (file.isFile() && file.canWrite() && name.endsWith(".log")) {\r
long lastModified = file.lastModified();\r
if (lastModified > 0 && lastModified < purgeThresold) {\r
- file.delete();\r
- logger.log(Level.INFO, "remove file " + file);\r
+ boolean result = file.delete();\r
+ logger.log(Level.INFO, "remove file " + file\r
+ + "/succeeded=" + result);\r
}\r
}\r
\r
gbc.weighty = 0.;\r
chkApplyAll = new JCheckBox(strings.getProperty("checkbox.applyAllItems"));\r
chkApplyAll.setSelected(!partsCategory.isMultipleSelectable());\r
+ chkApplyAll.addActionListener(new ActionListener() {\r
+ public void actionPerformed(ActionEvent e) {\r
+ // すべてに適用のチェックが変更された場合は全レイヤーの色の変更通知を出す.\r
+ apply();\r
+ }\r
+ });\r
btnPanel.add(chkApplyAll, gbc);\r
\r
gbc.gridx = colIdx++;\r
\r
protected boolean isComplete() {\r
\r
- if (basicPanel.isExportSubset() && !basicPanel.isExportPartsImages()\r
- && !basicPanel.isExportPresets()\r
- && !basicPanel.isExportSamplePicture()) {\r
- // サブセットを指定した場合、パーツイメージ、パーツセット、またはサンプルピクチャのいずれかはエクスポートする必要がある.\r
- return false;\r
- }\r
- \r
if (basicPanel.isExportPartsImages()) {\r
if (partsSelectPanel.getSelectedCount() == 0) {\r
// パーツイメージのエクスポートを指定した場合、エクスポートするパーツの選択は必須\r
boolean exportSamplePicture = basicPanel.isExportSamplePicture();\r
boolean exportCharacterData = true;\r
boolean exportPartsImages = basicPanel.isExportPartsImages();\r
- boolean exportSubset = basicPanel.isExportSubset();\r
\r
// 基本情報を設定する.\r
cd.setAuthor(basicPanel.getAuthor());\r
exportProp.setProperty(ExportInfoKeys.EXPORT_PRESETS, Boolean.toString(exportPresets));\r
exportProp.setProperty(ExportInfoKeys.EXPORT_SAMPLE_PICTURE, Boolean.toString(exportSamplePicture));\r
exportProp.setProperty(ExportInfoKeys.EXPORT_CHARACTER_DATA, Boolean.toString(exportCharacterData));\r
- exportProp.setProperty(ExportInfoKeys.EXPORT_SUBSET, Boolean.toString(exportSubset));\r
exportProp.setProperty(ExportInfoKeys.EXPORT_PARTS_IMAGES, Boolean.toString(exportPartsImages));\r
exportProp.setProperty(ExportInfoKeys.EXPORT_TIMESTAMP, Long.toString(System.currentTimeMillis()));\r
\r
\r
BufferedImage getSamplePicture();\r
\r
- boolean isExportSubset();\r
- \r
boolean isExportSamplePicture();\r
\r
boolean isExportPartsImages();\r
\r
private JCheckBox chkSampleImage;\r
\r
- private JCheckBox chkExportSubset;\r
- \r
\r
\r
protected ExportInformationPanel(final CharacterData characterData, final BufferedImage samplePicture) {\r
gbc.gridx = 0;\r
gbc.gridy = 0;\r
gbc.gridheight = 1;\r
- gbc.gridwidth = 2;\r
+ gbc.gridwidth = 1;\r
gbc.weightx = 0.;\r
gbc.weighty = 0.;\r
gbc.insets = new Insets(3, 3, 3, 3);\r
gbc.anchor = GridBagConstraints.WEST;\r
gbc.fill = GridBagConstraints.BOTH;\r
- chkExportSubset = new JCheckBox(strings.getProperty("exportSubset"));\r
- commentPanel.add(chkExportSubset, gbc);\r
-\r
- gbc.gridx = 0;\r
- gbc.gridy = 1;\r
- gbc.gridheight = 1;\r
- gbc.gridwidth = 1;\r
- gbc.weightx = 0.;\r
- gbc.weighty = 0.;\r
commentPanel.add(new JLabel(strings.getProperty("author"), JLabel.RIGHT), gbc);\r
\r
gbc.gridx = 1;\r
- gbc.gridy = 1;\r
+ gbc.gridy = 0;\r
gbc.gridwidth = 1;\r
gbc.weightx = 1.;\r
+ gbc.weighty = 0.;\r
txtAuthor = new JTextField();\r
commentPanel.add(txtAuthor, gbc);\r
\r
gbc.gridx = 0;\r
- gbc.gridy = 2;\r
+ gbc.gridy = 1;\r
gbc.gridwidth = 1;\r
gbc.gridheight = 1;\r
gbc.weightx = 0.;\r
+ gbc.weighty = 0.;\r
commentPanel.add(new JLabel(strings.getProperty("description"), JLabel.RIGHT), gbc);\r
\r
gbc.gridx = 1;\r
- gbc.gridy = 2;\r
+ gbc.gridy = 1;\r
gbc.gridwidth = 1;\r
- gbc.gridheight = 5;\r
+ gbc.gridheight = 2;\r
gbc.weighty = 1.;\r
gbc.weightx = 1.;\r
txtDescription = new JTextArea();\r
chkPartsImages.addActionListener(modListener);\r
chkPresets.addActionListener(modListener);\r
chkSampleImage.addActionListener(modListener);\r
- chkExportSubset.addActionListener(modListener);\r
}\r
\r
protected void updateSamplePicture() {\r
String description = characterData.getDescription();\r
txtAuthor.setText(author == null ? "" : author);\r
txtDescription.setText(description == null ? "" : description);\r
- \r
- chkExportSubset.setSelected(true);\r
}\r
\r
public BufferedImage getSamplePicture() {\r
return samplePicture;\r
}\r
\r
- public boolean isExportSubset() {\r
- return chkExportSubset.isSelected();\r
- }\r
- \r
public boolean isExportSamplePicture() {\r
return chkSampleImage.isSelected();\r
}\r
import java.awt.Dimension;\r
import java.awt.Font;\r
import java.awt.Frame;\r
+import java.awt.GraphicsEnvironment;\r
import java.awt.Point;\r
+import java.awt.Rectangle;\r
import java.awt.Toolkit;\r
import java.awt.dnd.DropTarget;\r
import java.awt.event.ActionEvent;\r
return activedMainFrame;\r
}\r
\r
+ public static void notifyImportedPartsOrFavorites(CharacterData cd,\r
+ CharacterData newCd, Component caller) throws IOException {\r
+ if (cd == null || newCd == null || caller == null) {\r
+ throw new IllegalArgumentException();\r
+ }\r
+\r
+ if (!cd.isValid() || !newCd.isValid()) {\r
+ // 変更前もしくは変更後が無効なキャラクターデータであれば\r
+ // 反映する必要ない.\r
+ return;\r
+ }\r
+ logger.log(Level.INFO, "parts imported for active profiles: " + newCd);\r
+ \r
+\r
+ if ( !cd.isSameStructure(newCd)) {\r
+ // キャラクターデータそのものが変更されている場合\r
+ notifyChangeCharacterData(cd, newCd, caller);\r
+\r
+ } else {\r
+ // パーツ構成は変更されていない場合\r
+ \r
+ // Frameのうち、ネイティブリソースと関連づけられているアクティブなフレームを調査\r
+ for (Frame frame : JFrame.getFrames()) {\r
+ if (frame.isDisplayable() && frame instanceof MainFrame) {\r
+ MainFrame mainFrame = (MainFrame) frame;\r
+ if (mainFrame.characterData == null || !mainFrame.characterData.isValid()) {\r
+ // 無効なキャラクターデータを保持している場合は、そのメインフレームは処理対象外\r
+ continue;\r
+ }\r
+ // パーツ及びお気に入りを再取得する場合.\r
+ caller.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));\r
+ try {\r
+ mainFrame.reloadPartsAndFavorites(newCd, true);\r
+ \r
+ } finally {\r
+ caller.setCursor(Cursor.getDefaultCursor());\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
\r
/**\r
* キャラクターデータが変更されたことを通知される.<br>\r
* @param cd キャラクターデータ(変更前)\r
* @param newCd キャラクターデータ(変更後)\r
+ * @param caller 呼び出しもとコンポーネント(ウェイトカーソル表示用)\r
* @param structureCompatible 構造が同一であるか?\r
* @throws IOException 新しいキャラクターデータのパーツセットのロードに失敗した場合 (メインフレームは変更されていません.)\r
*/\r
- public static void notifyChangeCharacterData(CharacterData cd, CharacterData newCd) throws IOException {\r
- if (cd == null || newCd == null) {\r
+ public static void notifyChangeCharacterData(CharacterData cd,\r
+ CharacterData newCd, Component caller) throws IOException {\r
+ if (cd == null || newCd == null || caller == null) {\r
throw new IllegalArgumentException();\r
}\r
\r
// 反映する必要ない.\r
return;\r
}\r
+ logger.log(Level.INFO, "change active profile: " + newCd);\r
\r
if (!ProfileListManager.isUsingCharacterData(cd)) {\r
// 使用中のプロファイルではないので何もしない.\r
return;\r
}\r
-\r
- // キャラクターデータが、まだ読み込まれていなければ読み込む.\r
- if (!newCd.isPartsLoaded()) {\r
- ProfileListManager.loadCharacterData(newCd);\r
- ProfileListManager.loadFavorites(newCd);\r
- }\r
\r
- // Frameのうち、ネイティブリソースと関連づけられているアクティブなフレームを調査\r
- for (Frame frame : JFrame.getFrames()) {\r
- if (frame.isDisplayable() && frame instanceof MainFrame) {\r
- MainFrame mainFrame = (MainFrame) frame;\r
- if (mainFrame.characterData == null || !mainFrame.characterData.isValid()) {\r
- // 無効なキャラクターデータを保持している場合は、そのメインフレームは処理対象外\r
- continue;\r
- }\r
-\r
- // メインフレームが保持しているキャラクターデータのdocbaseと\r
- // 変更対象となったキャラクターデータのdocbaseが等しければ、メインフレームを更新する必要がある.\r
- String docbaseOrg = cd.getDocBase().toString();\r
- String docbaseMine = mainFrame.characterData.getDocBase().toString();\r
- if (docbaseOrg.equals(docbaseMine)) {\r
- mainFrame.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));\r
- try {\r
- // 現在情報の保存\r
- mainFrame.saveWorkingSet();\r
-\r
- // 画面構成の再構築\r
- mainFrame.initComponent(newCd);\r
+ caller.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));\r
+ try {\r
+ // キャラクターデータが、まだ読み込まれていなければ読み込む.\r
+ if (!newCd.isPartsLoaded()) {\r
+ ProfileListManager.loadCharacterData(newCd);\r
+ ProfileListManager.loadFavorites(newCd);\r
+ }\r
+ \r
+ // Frameのうち、ネイティブリソースと関連づけられているアクティブなフレームを調査\r
+ for (Frame frame : JFrame.getFrames()) {\r
+ if (frame.isDisplayable() && frame instanceof MainFrame) {\r
+ MainFrame mainFrame = (MainFrame) frame;\r
+ if (mainFrame.characterData == null || !mainFrame.characterData.isValid()) {\r
+ // 無効なキャラクターデータを保持している場合は、そのメインフレームは処理対象外\r
+ continue;\r
+ }\r
\r
- } finally {\r
- mainFrame.setCursor(Cursor.getDefaultCursor());\r
+ // メインフレームが保持しているキャラクターデータのdocbaseと\r
+ // 変更対象となったキャラクターデータのdocbaseが等しければ、メインフレームを更新する必要がある.\r
+ String docbaseOrg = cd.getDocBase().toString();\r
+ String docbaseMine = mainFrame.characterData.getDocBase().toString();\r
+ if (docbaseOrg.equals(docbaseMine)) {\r
+ mainFrame.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));\r
+ try {\r
+ // 現在情報の保存\r
+ mainFrame.saveWorkingSet();\r
+\r
+ // 画面構成の再構築\r
+ mainFrame.initComponent(newCd);\r
+\r
+ } finally {\r
+ if (mainFrame != caller) {\r
+ mainFrame.setCursor(Cursor.getDefaultCursor());\r
+ }\r
+ }\r
}\r
}\r
}\r
+\r
+ } finally {\r
+ caller.setCursor(Cursor.getDefaultCursor());\r
}\r
}\r
\r
* @param characterData キャラクターデータ\r
*/\r
public MainFrame(CharacterData characterData) {\r
- if (characterData == null) {\r
- throw new IllegalArgumentException();\r
- }\r
- \r
- setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);\r
- addWindowListener(new WindowAdapter() {\r
- @Override\r
- public void windowClosing(WindowEvent e) {\r
- onCloseProfile();\r
+ try {\r
+ if (characterData == null) {\r
+ throw new IllegalArgumentException();\r
}\r
- @Override\r
- public void windowClosed(WindowEvent e) {\r
- imageBuilder.stop();\r
+ \r
+ setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);\r
+ addWindowListener(new WindowAdapter() {\r
+ @Override\r
+ public void windowClosing(WindowEvent e) {\r
+ onCloseProfile();\r
+ }\r
+ @Override\r
+ public void windowClosed(WindowEvent e) {\r
+ stopAgents();\r
+ }\r
+ @Override\r
+ public void windowActivated(WindowEvent e) {\r
+ setActivedMainFrame(MainFrame.this);\r
+ }\r
+ @Override\r
+ public void windowOpened(WindowEvent e) {\r
+ // do nothing.\r
+ }\r
+ });\r
+\r
+ icon = UIHelper.getInstance().getImage("icons/icon.png");\r
+ setIconImage(icon);\r
+ \r
+ // 画面コンポーネント作成\r
+ initComponent(characterData);\r
+ JMenuBar menuBar = createMenuBar();\r
+ setJMenuBar(menuBar);\r
+ \r
+ // メインスクリーンサイズを取得する.\r
+ GraphicsEnvironment genv = GraphicsEnvironment.getLocalGraphicsEnvironment();\r
+ Rectangle desktopSize = genv.getMaximumWindowBounds(); // メインスクリーンのサイズ(デスクトップ領域のみ)\r
+ logger.log(Level.INFO, "desktopSize=" + desktopSize);\r
+ \r
+ Dimension imageSize = characterData.getImageSize();\r
+ // 画像サイズ300x400を基準サイズとして、それ以下にはならない.\r
+ // アプリケーション設定の最大サイズ以上の場合はウィンドウサイズは固定してスクロールバーに任せる\r
+ AppConfig appConfig = AppConfig.getInstance();\r
+ int maxWidth = min(desktopSize.width, appConfig.getMainFrameMaxWidth());\r
+ int maxHeight = min(desktopSize.height, appConfig.getMainFrameMaxHeight());\r
+ int imageWidth = min(maxWidth, max(300, imageSize != null ? imageSize.width : 0));\r
+ int imageHeight = min(maxHeight, max(400, imageSize != null ? imageSize.height : 0));\r
+ // 300x400の画像の場合にメインフレームが600x550だとちょうどいい感じ.\r
+ // それ以上大きい画像の場合は増えた分だけフレームを大きくしておく.\r
+ setSize(imageWidth - 300 + 600, imageHeight - 400 + 550);\r
+\r
+ // 次回表示時にプラットフォーム固有位置に表示するように予約\r
+ setLocationByPlatform(true);\r
+ \r
+ } catch (RuntimeException ex) {\r
+ logger.log(Level.SEVERE, "メインフレームの構築中に予期せぬ例外が発生しました。", ex);\r
+ dispose(); // コンストラクタが呼ばれた時点でJFrameは構築済みなのでdisposeの必要がある.\r
+ throw ex;\r
+ } catch (Error ex) {\r
+ logger.log(Level.SEVERE, "メインフレームの構築中に致命的な例外が発生しました。", ex);\r
+ dispose(); // コンストラクタが呼ばれた時点でJFrameは構築済みなのでdisposeの必要がある.\r
+ throw ex;\r
+ }\r
+ }\r
+ \r
+ /**\r
+ * メインフレームを表示する.<br>\r
+ * デスクトップ領域からはみ出した場合は位置を補正する.<br>\r
+ */\r
+ public void showMainFrame() {\r
+ // メインスクリーンサイズを取得する.\r
+ GraphicsEnvironment genv = GraphicsEnvironment.getLocalGraphicsEnvironment();\r
+ Rectangle desktopSize = genv.getMaximumWindowBounds(); // メインスクリーンのサイズ(デスクトップ領域のみ)\r
+ logger.log(Level.INFO, "desktopSize=" + desktopSize);\r
+\r
+ // プラットフォーム固有の位置あわせで表示する.\r
+ // 表示した結果、はみ出している場合は0,0に補正する.\r
+ setVisible(true);\r
+ Point loc = getLocation();\r
+ logger.log(Level.INFO, "windowLocation=" + loc);\r
+ Dimension windowSize = getSize();\r
+ if (loc.y + windowSize.height >= desktopSize.height) {\r
+ loc.y = 0;\r
+ }\r
+ if (loc.x + windowSize.width >= desktopSize.width) {\r
+ loc.x = 0;\r
+ }\r
+ if (loc.x == 0 || loc.y == 0) {\r
+ setLocation(loc);\r
+ }\r
+\r
+ // デスクトップよりも大きい場合は小さくする.\r
+ boolean resize = false;\r
+ Dimension dim = getSize();\r
+ if (dim.height > desktopSize.height) {\r
+ dim.height = desktopSize.height;\r
+ resize = true;\r
+ }\r
+ if (dim.width > desktopSize.width) {\r
+ dim.width = desktopSize.width;\r
+ resize = true;\r
+ }\r
+ if (resize) {\r
+ setSize(dim);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * このメインフレームに関連づけられているエージェントスレッドを停止します.<br>\r
+ * すでに停止している場合は何もしません。\r
+ */\r
+ protected void stopAgents() {\r
+ // エージェントを停止\r
+ if (watchAgent != null) {\r
+ try {\r
watchAgent.stop();\r
+ \r
+ } catch (Throwable ex) {\r
+ logger.log(Level.SEVERE, "フォルダ監視スレッドの停止に失敗しました。", ex);\r
}\r
- @Override\r
- public void windowActivated(WindowEvent e) {\r
- setActivedMainFrame(MainFrame.this);\r
- }\r
- @Override\r
- public void windowOpened(WindowEvent e) {\r
- // do nothing.\r
+ watchAgent = null;\r
+ }\r
+ // イメージビルダを停止\r
+ if (imageBuilder != null) {\r
+ try {\r
+ imageBuilder.stop();\r
+\r
+ } catch (Throwable ex) {\r
+ logger.log(Level.SEVERE, "非同期イメージビルダスレッドの停止に失敗しました。", ex);\r
}\r
- });\r
+ imageBuilder = null;\r
+ }\r
+ }\r
\r
- icon = UIHelper.getInstance().getImage("icons/icon.png");\r
- setIconImage(icon);\r
- \r
- // 画面コンポーネント作成\r
- initComponent(characterData);\r
- JMenuBar menuBar = createMenuBar();\r
- setJMenuBar(menuBar);\r
-\r
- Dimension imageSize = characterData.getImageSize();\r
- // 画像サイズ300x400を基準サイズとして、それ以下にはならない.\r
- // アプリケーション設定の最大サイズ以上の場合はウィンドウサイズは固定してスクロールバーに任せる\r
- AppConfig appConfig = AppConfig.getInstance();\r
- int maxWidth = appConfig.getMainFrameMaxWidth();\r
- int maxHeight = appConfig.getMainFrameMaxHeight();\r
- int imageWidth = min(maxWidth, max(300, imageSize != null ? imageSize.width : 0));\r
- int imageHeight = min(maxHeight, max(400, imageSize != null ? imageSize.height : 0));\r
- // 300x400の画像の場合にメインフレームが600x550だとちょうどいい感じ.\r
- // それ以上大きい画像の場合は増えた分だけフレームを大きくしておく.\r
- setSize(imageWidth - 300 + 600, imageHeight - 400 + 550);\r
-\r
- //setLocationRelativeTo(null);\r
- setLocationByPlatform(true);\r
+ /**\r
+ * メインフレームを破棄します.<br>\r
+ */\r
+ @Override\r
+ public void dispose() {\r
+ stopAgents();\r
+ super.dispose();\r
}\r
\r
/**\r
* 画面コンポーネントを設定します.<br>\r
* すでに設定されている場合は一旦削除されたのちに再作成されます.<br>\r
*/\r
- private void initComponent(CharacterData characterData) {\r
+ private synchronized void initComponent(CharacterData characterData) {\r
\r
CharacterData oldCd;\r
synchronized (this) {\r
\r
// デフォルトのパーツセット表示名\r
defaultPartsSetTitle = strings.getProperty("defaultPartsSetTitle");\r
- \r
- // エージェントを停止\r
- if (watchAgent != null) {\r
- watchAgent.stop();\r
- }\r
- // イメージビルダを停止\r
- if (imageBuilder != null) {\r
- imageBuilder.stop();\r
- }\r
+\r
+ // エージェントの停止\r
+ stopAgents();\r
\r
// コンポーネント配置\r
Container contentPane = getContentPane();\r
// 保存されているワーキングセットを復元する.\r
// 復元できなかった場合はパーツセットを初期選択する.\r
if ( !loadWorkingSet()) {\r
- showDefaultParts(true);\r
+ if (showDefaultParts(true)) {\r
+ requestPreview();\r
+ }\r
}\r
\r
// 選択されているパーツを見える状態にする\r
* パーツデータをリロードし、各カテゴリのパーツ一覧を再表示させ、プレビューを更新する.<br>\r
*/\r
protected void onDetectPartsImageChange() {\r
- if (characterData.reloadPartsData()) {\r
- partsSelectionManager.loadParts();\r
- requestPreview();\r
+ try {\r
+ reloadPartsAndFavorites(null, true);\r
+\r
+ } catch (IOException ex) {\r
+ logger.log(Level.SEVERE, "parts reload failed. " + characterData, ex);\r
}\r
}\r
\r
/**\r
* デフォルトパーツを選択する.<br>\r
* デフォルトパーツがなければお気に入りの最初のものを選択する.<br>\r
- * それもなければ空として表示する.\r
+ * それもなければ空として表示する.<br>\r
+ * パーツの適用に失敗した場合はfalseを返します.(例外は返されません.)<br>\r
* @param force すでに選択があっても選択しなおす場合はtrue、falseの場合は選択があれば何もしない.\r
- * @return ã\83\91ã\83¼ã\83\84é\81¸æ\8a\9eã\81\95ã\82\8cã\81ªã\81\8bã\81£ã\81\9få ´å\90\88ã\80\82force=trueã\81®å ´å\90\88ã\81¯常にtrueとなります。\r
+ * @return ã\83\91ã\83¼ã\83\84é\81¸æ\8a\9eã\81\95ã\82\8cã\81\9få ´å\90\88ã\80\82force=trueã\81®å ´å\90\88ã\81¯ã\82¨ã\83©ã\83¼ã\81\8cã\81ªã\81\91ã\82\8cã\81°常にtrueとなります。\r
*/\r
protected boolean showDefaultParts(boolean force) {\r
- if (!force) {\r
- PartsSet sel = partsSelectionManager.createPartsSet();\r
- if (!sel.isEmpty()) {\r
- // 強制選択でない場合、すでに選択済みのパーツがあれば何もしない.\r
- return false;\r
+ try {\r
+ if (!force) {\r
+ // 現在選択中のパーツを取得する.(なければ空)\r
+ PartsSet sel = partsSelectionManager.createPartsSet();\r
+ if (!sel.isEmpty()) {\r
+ // 強制選択でない場合、すでに選択済みのパーツがあれば何もしない.\r
+ return false;\r
+ }\r
}\r
- }\r
-\r
- // デフォルトのパーツセットを取得する\r
- String defaultPresetId = characterData.getDefaultPartsSetId();\r
- PartsSet partsSet = null;\r
- if (defaultPresetId != null) {\r
- partsSet = characterData.getPartsSets().get(defaultPresetId);\r
- }\r
-\r
- // デフォルトのパーツセットがなければ、お気に入りの最初を選択する.\r
- if (partsSet == null) {\r
- List<PartsSet> partssets = getPartsSetList();\r
- if (!partssets.isEmpty()) {\r
- partsSet = partssets.get(0);\r
+ \r
+ // デフォルトのパーツセットを取得する\r
+ String defaultPresetId = characterData.getDefaultPartsSetId();\r
+ PartsSet partsSet = null;\r
+ if (defaultPresetId != null) {\r
+ partsSet = characterData.getPartsSets().get(defaultPresetId);\r
}\r
- }\r
+ \r
+ // デフォルトのパーツセットがなければ、お気に入りの最初を選択する.\r
+ if (partsSet == null) {\r
+ List<PartsSet> partssets = getPartsSetList();\r
+ if (!partssets.isEmpty()) {\r
+ partsSet = partssets.get(0);\r
+ }\r
+ }\r
+ \r
+ // パーツセットがあれば、それを表示要求する.\r
+ // パーツセットがなければカラーダイアログを初期化するのみ\r
+ if (partsSet == null) {\r
+ partsColorCoordinator.initColorDialog();\r
\r
- // パーツセットがあれば、それを表示要求する.\r
- // パーツセットがなければカラーダイアログを初期化するのみ\r
- if (partsSet == null) {\r
- partsColorCoordinator.initColorDialog();\r
- requestPreview();\r
- } else {\r
- selectPresetParts(partsSet);\r
+ } else {\r
+ selectPresetParts(partsSet);\r
+ }\r
+ \r
+ } catch (Exception ex) {\r
+ logger.log(Level.WARNING, "パーツのデフォルト適用に失敗しました。", ex);\r
+ return false;\r
}\r
- \r
return true;\r
}\r
\r
/**\r
- * プリセットを適用しキャラクターイメージを再構築します.\r
- * @param presetParts\r
+ * プリセットを適用しキャラクターイメージを再構築します.<br>\r
+ * 実行時エラーは画面のレポートされます.<br>\r
+ * @param presetParts パーツセット, nullの場合は何もしない.\r
*/\r
protected void selectPresetParts(PartsSet presetParts) {\r
- // 最後に使用したプリセットとして記憶する.\r
- lastUsePresetParts = presetParts;\r
- // プリセットパーツで選択を変える\r
- partsSelectionManager.selectPartsSet(presetParts);\r
- // カラーパネルを選択されているアイテムをもとに再設定する\r
- partsColorCoordinator.initColorDialog();\r
- // 再表示\r
- requestPreview();\r
+ if (presetParts == null) {\r
+ return;\r
+ }\r
+ try {\r
+ // 最後に使用したプリセットとして記憶する.\r
+ lastUsePresetParts = presetParts;\r
+ // プリセットパーツで選択を変える\r
+ partsSelectionManager.selectPartsSet(presetParts);\r
+ // カラーパネルを選択されているアイテムをもとに再設定する\r
+ partsColorCoordinator.initColorDialog();\r
+ // 再表示\r
+ requestPreview();\r
+ \r
+ } catch (Exception ex) {\r
+ ErrorMessageHelper.showErrorDialog(this, ex);\r
+ }\r
}\r
\r
/**\r
try {\r
MainFrame main2 = ProfileListManager.openProfile(this);\r
if (main2 != null) {\r
- Point pt = getLocation();\r
- pt.x += 100;\r
- main2.setLocation(pt);\r
- main2.setVisible(true);\r
+ main2.showMainFrame();\r
}\r
\r
} catch (Exception ex) {\r
CharacterData cd = this.characterData;\r
CharacterData newCd = ProfileListManager.editProfile(this, cd);\r
if (newCd != null) {\r
- setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));\r
- try {\r
- MainFrame.notifyChangeCharacterData(cd, newCd); //TODO:重複\r
-\r
- } finally {\r
- setCursor(Cursor.getDefaultCursor());\r
- }\r
+ MainFrame.notifyChangeCharacterData(cd, newCd, this);\r
}\r
\r
} catch (Exception ex) {\r
return;\r
}\r
\r
- // 監視スレッドを停止してからインポート処理を始める\r
- boolean stopped = watchAgent.stop();\r
try {\r
// インポートウィザードの実行\r
ImportWizardDialog importWizDialog = new ImportWizardDialog(this, characterData, initFiles);\r
importWizDialog.setVisible(true);\r
\r
if (importWizDialog.getExitCode() == ImportWizardDialog.EXIT_PROFILE_UPDATED) {\r
- try {\r
- setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));\r
- try {\r
- // インポートが実行された場合、パーツデータをリロードする.\r
- if (characterData.reloadPartsData()) {\r
- partsSelectionManager.loadParts();\r
-\r
- // パーツデータが変更された可能性があるので、\r
- // 監視状態をリセットしてから再開するようにする.\r
- watchAgent.reset();\r
- }\r
- \r
- // お気に入りをリロードする.\r
- CharacterDataPersistent persiste = CharacterDataPersistent.getInstance();\r
- persiste.loadFavorites(characterData);\r
- \r
- } finally {\r
- setCursor(Cursor.getDefaultCursor());\r
- }\r
-\r
- } catch (Exception ex) {\r
- ErrorMessageHelper.showErrorDialog(this, ex);\r
- }\r
-\r
- // 再表示\r
- if (showDefaultParts(false)) {\r
- requestPreview();\r
- }\r
+ CharacterData importedCd = importWizDialog.getImportedCharacterData();\r
+ notifyImportedPartsOrFavorites(characterData, importedCd, this);\r
}\r
\r
- } finally {\r
- // 監視スレッドを再開する.\r
- if (stopped) {\r
- watchAgent.start();\r
+ } catch (Exception ex) {\r
+ ErrorMessageHelper.showErrorDialog(this, ex);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * パーツとお気に入りをリロードする.<br>\r
+ * まだロードされていない場合はあらたにロードする.<br>\r
+ * 引数newCdが指定されている場合は、現在のキャラクター定義の説明文を更新する.<br>\r
+ * (説明文の更新以外には使用されない.)<br>\r
+ * \r
+ * @param newCd\r
+ * 説明文更新のための更新されたキャラクターデータを指定する。null可\r
+ * @param forceRepaint\r
+ * 必ず再描画する場合\r
+ * @throws IOException\r
+ * 失敗\r
+ */\r
+ protected synchronized void reloadPartsAndFavorites(CharacterData newCd,\r
+ boolean forceRepaint) throws IOException {\r
+ if (newCd != null) {\r
+ // (インポート画面では説明文のみ更新するので、それだけ取得)\r
+ characterData.setDescription(newCd.getDescription());\r
+ }\r
+ \r
+ if ( !characterData.isPartsLoaded()) {\r
+ // キャラクターデータが、まだ読み込まれていなければ読み込む.\r
+ ProfileListManager.loadCharacterData(characterData);\r
+ ProfileListManager.loadFavorites(characterData);\r
+ partsSelectionManager.loadParts();\r
+\r
+ } else {\r
+ // パーツデータをリロードする.\r
+ if (characterData.reloadPartsData()) {\r
+ partsSelectionManager.loadParts();\r
}\r
+\r
+ // お気に入りをリロードする.\r
+ CharacterDataPersistent persiste = CharacterDataPersistent.getInstance();\r
+ persiste.loadFavorites(characterData);\r
+ notifyChangeFavorites(characterData);\r
+ }\r
+\r
+ // 現在選択されているパーツセットがない場合はデフォルトのパーツセットを選択する.\r
+ if (showDefaultParts(false) || forceRepaint) {\r
+ requestPreview();\r
}\r
}\r
\r
\r
// 現在のパーツ色情報にワーキングセットで保存した内容を設定する.\r
Map<PartsIdentifier, PartsColorInfo> partsColorInfoMap = characterData.getPartsColorManager().getPartsColorInfoMap();\r
- for (Map.Entry<PartsIdentifier, PartsColorInfo> entry : workingSet.getPartsColorInfoMap().entrySet()) {\r
- PartsIdentifier partsIdentifier = entry.getKey();\r
- PartsColorInfo partsColorInfo = entry.getValue();\r
- partsColorInfoMap.put(partsIdentifier, partsColorInfo);\r
+ Map<PartsIdentifier, PartsColorInfo> workingPartsColorInfoMap = workingSet.getPartsColorInfoMap();\r
+ if (workingPartsColorInfoMap != null) {\r
+ for (Map.Entry<PartsIdentifier, PartsColorInfo> entry : workingPartsColorInfoMap.entrySet()) {\r
+ PartsIdentifier partsIdentifier = entry.getKey();\r
+ PartsColorInfo partsColorInfo = entry.getValue();\r
+ partsColorInfoMap.put(partsIdentifier, partsColorInfo);\r
+ }\r
}\r
\r
// 選択されているパーツの復元\r
PartsSet partsSet = workingSet.getPartsSet();\r
if (partsSet != null) {\r
- selectPresetParts(partsSet.createCompatible(characterData));\r
+ partsSet = partsSet.createCompatible(characterData);\r
+ selectPresetParts(partsSet);\r
\r
// 最後に選択したお気に入り情報の復元\r
PartsSet lastUsePresetParts = workingSet.getLastUsePresetParts();\r
}\r
}),\r
null,\r
- new MenuDataFactory("file.manageParts", new ActionListener() {\r
+ new MenuDataFactory("file.editprofile", new ActionListener() {\r
public void actionPerformed(ActionEvent e) {\r
- onManageParts();\r
+ onEditProfile();\r
}\r
}),\r
new MenuDataFactory("file.opendir", new ActionListener() {\r
onBrowseProfileDir();\r
}\r
}),\r
- new MenuDataFactory("file.editprofile", new ActionListener() {\r
- public void actionPerformed(ActionEvent e) {\r
- onEditProfile();\r
- }\r
- }),\r
new MenuDataFactory("file.import", new MenuDataFactory[] {\r
new MenuDataFactory("file.importMe", new ActionListener() {\r
public void actionPerformed(ActionEvent e) {\r
onExport();\r
};\r
}),\r
+ new MenuDataFactory("file.manageParts", new ActionListener() {\r
+ public void actionPerformed(ActionEvent e) {\r
+ onManageParts();\r
+ }\r
+ }),\r
new MenuDataFactory("file.preferences", new ActionListener() {\r
public void actionPerformed(ActionEvent e) {\r
onPreferences();\r
import com.apple.eawt.ApplicationAdapter;
import com.apple.eawt.ApplicationEvent;
+/**
+ * Mac OS X用のメインフレームサポートクラス.<br>
+ * スクリーンメニューのハンドラなどを接続している.<br>
+ * @author seraphy
+ *
+ */
public class MainFramePartialForMacOSX {
/**
newCd.setRev(persist.generateRev());\r
}\r
\r
- } else {\r
- // 新規ではなく、構造が変更されていることを通知する.\r
+ } else if ( !newCd.isUpperCompatibleStructure(original)){\r
+ // 上位互換のない構造が変更されていることを通知する.\r
if (JOptionPane.showConfirmDialog(this,\r
strings.get("confirm.changestructre"),\r
strings.getProperty("confirm"),\r
}\r
\r
// 現在開いているメインフレームに対してキャラクター定義が変更されたことを通知する.\r
- setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));\r
- try {\r
- MainFrame.notifyChangeCharacterData(cd, newCd); //TODO:重複\r
-\r
- } finally {\r
- setCursor(Cursor.getDefaultCursor());\r
- }\r
+ MainFrame.notifyChangeCharacterData(cd, newCd, this);\r
\r
// プロファイル一覧画面も更新する.\r
characterListModel.set(rowIndex, newCd);\r
* インポート\r
*/\r
protected void onProfileImport() {\r
- CharacterData selCd = (CharacterData) characterList.getSelectedValue();\r
-\r
- // 選択したプロファイルを更新するか、新規にプロファイルを作成するか選択できるようにする\r
- if (selCd != null) {\r
- final Properties strings = LocalizedResourcePropertyLoader\r
- .getInstance().getLocalizedProperties(STRINGS_RESOURCE);\r
-\r
- JPanel radioPanel = new JPanel(new BorderLayout());\r
- JRadioButton btnUpdate = new JRadioButton(strings.getProperty("importToUpdateProfile"));\r
- JRadioButton btnNew = new JRadioButton(strings.getProperty("importToCreateProfile"));\r
- ButtonGroup radios = new ButtonGroup();\r
- radios.add(btnUpdate);\r
- radios.add(btnNew);\r
- btnUpdate.setSelected(true);\r
- radioPanel.add(btnUpdate, BorderLayout.NORTH);\r
- radioPanel.add(btnNew, BorderLayout.SOUTH);\r
+ try {\r
+ CharacterData selCd = (CharacterData) characterList.getSelectedValue();\r
+\r
+ // 選択したプロファイルを更新するか、新規にプロファイルを作成するか選択できるようにする\r
+ if (selCd != null) {\r
+ final Properties strings = LocalizedResourcePropertyLoader\r
+ .getInstance().getLocalizedProperties(STRINGS_RESOURCE);\r
+\r
+ JPanel radioPanel = new JPanel(new BorderLayout());\r
+ JRadioButton btnUpdate = new JRadioButton(strings.getProperty("importToUpdateProfile"));\r
+ JRadioButton btnNew = new JRadioButton(strings.getProperty("importToCreateProfile"));\r
+ ButtonGroup radios = new ButtonGroup();\r
+ radios.add(btnUpdate);\r
+ radios.add(btnNew);\r
+ btnUpdate.setSelected(true);\r
+ radioPanel.add(btnUpdate, BorderLayout.NORTH);\r
+ radioPanel.add(btnNew, BorderLayout.SOUTH);\r
+ \r
+ int ret = JOptionPane.showConfirmDialog(this, radioPanel,\r
+ strings.getProperty("confirmUpdateProfile"),\r
+ JOptionPane.OK_CANCEL_OPTION);\r
+ if (ret != JOptionPane.OK_OPTION) {\r
+ return;\r
+ }\r
\r
- int ret = JOptionPane.showConfirmDialog(this, radioPanel,\r
- strings.getProperty("confirmUpdateProfile"),\r
- JOptionPane.OK_CANCEL_OPTION);\r
- if (ret != JOptionPane.OK_OPTION) {\r
- return;\r
- }\r
- \r
- if (btnNew.isSelected()) {\r
- // 選択されていないことにする.\r
- selCd = null;\r
+ if (btnNew.isSelected()) {\r
+ // 選択されていないことにする.\r
+ selCd = null;\r
+ }\r
}\r
- }\r
- \r
- // キャラクターデータをロードし直す.\r
- CharacterData cd;\r
- if (selCd != null) {\r
- cd = selCd.duplicateBasicInfo();\r
- try {\r
- ProfileListManager.loadCharacterData(cd);\r
- ProfileListManager.loadFavorites(cd);\r
- \r
- } catch (IOException ex) {\r
- ErrorMessageHelper.showErrorDialog(this, ex);\r
- // 継続する.\r
+ \r
+ // キャラクターデータをロードし直す.\r
+ CharacterData cd;\r
+ if (selCd != null) {\r
+ cd = selCd.duplicateBasicInfo();\r
+ try {\r
+ ProfileListManager.loadCharacterData(cd);\r
+ ProfileListManager.loadFavorites(cd);\r
+ \r
+ } catch (IOException ex) {\r
+ ErrorMessageHelper.showErrorDialog(this, ex);\r
+ // 継続する.\r
+ }\r
+ } else {\r
+ cd = null;\r
}\r
- } else {\r
- cd = null;\r
- }\r
- \r
- // インポートウィザードの実行\r
- ImportWizardDialog importWizDialog = new ImportWizardDialog(this, cd);\r
- importWizDialog.setVisible(true);\r
- \r
- if (importWizDialog.getExitCode() == ImportWizardDialog.EXIT_PROFILE_CREATED) {\r
+ \r
+ // インポートウィザードの実行\r
+ ImportWizardDialog importWizDialog = new ImportWizardDialog(this, cd);\r
+ importWizDialog.setVisible(true);\r
+ \r
CharacterData newCd = importWizDialog.getImportedCharacterData();\r
+ if (importWizDialog.getExitCode() == ImportWizardDialog.EXIT_PROFILE_CREATED) {\r
+\r
+ // 作成されたプロファイルを一覧に追加する.\r
+ characterListModel.addElement(newCd);\r
+\r
+ } else if (importWizDialog.getExitCode() == ImportWizardDialog.EXIT_PROFILE_UPDATED) {\r
\r
- // 作成されたプロファイルを一覧に追加する.\r
- characterListModel.addElement(newCd);\r
+ // 更新されたプロファイルを通知する\r
+ MainFrame.notifyImportedPartsOrFavorites(cd, newCd, this);\r
+ }\r
+ \r
+ } catch (Exception ex) {\r
+ ErrorMessageHelper.showErrorDialog(this, ex);\r
}\r
}\r
\r
*/\r
public static void browse(final Component parent, final String url, final String description) {\r
try {\r
- browse(new URI(url));\r
-\r
- } catch (Exception ex) {\r
- ErrorMessageHelper.showErrorDialog(parent, ex);\r
- }\r
- try {\r
URI helpURI = new URI(url);\r
\r
if (!DesktopUtilities.browse(helpURI) ){\r
import java.io.File;\r
import java.net.URI;\r
import java.util.UUID;\r
+import java.util.logging.Level;\r
+import java.util.logging.Logger;\r
\r
\r
/**\r
* @author seraphy\r
*/\r
public class UserDataFactory {\r
+ \r
+ /**\r
+ * ロガー\r
+ */\r
+ private static final Logger logger = Logger.getLogger(UserDataFactory.class.getName());\r
\r
/**\r
* シングルトン\r
\r
// フォルダがなければ作成する.\r
if (!userDataDir.exists()) {\r
- userDataDir.mkdirs();\r
+ boolean result = userDataDir.mkdirs();\r
+ logger.log(Level.INFO, "makeDir: " + userDataDir + " /succeeded=" + result);\r
}\r
\r
return userDataDir;\r