OSDN Git Service

f7bfeff9dbd1b7090ec6d7ab4250e661120a14bf
[charactermanaj/CharacterManaJ.git] / src / main / java / charactermanaj / model / AppConfig.java
1 package charactermanaj.model;
2
3 import java.awt.Color;
4 import java.beans.PropertyChangeListener;
5 import java.beans.PropertyChangeSupport;
6 import java.io.BufferedOutputStream;
7 import java.io.File;
8 import java.io.FileNotFoundException;
9 import java.io.FileOutputStream;
10 import java.io.IOException;
11 import java.io.InputStream;
12 import java.io.OutputStream;
13 import java.net.URI;
14 import java.net.URL;
15 import java.nio.charset.Charset;
16 import java.util.ArrayList;
17 import java.util.Collections;
18 import java.util.HashMap;
19 import java.util.HashSet;
20 import java.util.List;
21 import java.util.Locale;
22 import java.util.Map;
23 import java.util.Properties;
24 import java.util.Set;
25 import java.util.logging.Level;
26 import java.util.logging.Logger;
27 import java.util.regex.Pattern;
28
29 import charactermanaj.util.ApplicationLogHandler;
30 import charactermanaj.util.BeanPropertiesUtilities;
31 import charactermanaj.util.BeanPropertiesUtilities.PropertyAccessor;
32 import charactermanaj.util.BeanPropertiesUtilities.PropertyAccessorMap;
33 import charactermanaj.util.ConfigurationDirUtilities;
34
35 /**
36  * アプリケーションの全域にわたる設定.<br>
37  * アプリケーション設定は、クラスパス上のリソース、コートベース直下のappConfig.xml、ユーザーごとのappConfig.xmlの順に読み込まれます.<br>
38  * 設定値は{@link BeanPropertiesUtilities}によってXMLプロパティファイルとして永続化されます。<br>
39  *
40  * @author seraphy
41  * @see BeanPropertiesUtilities
42  */
43 public final class AppConfig {
44
45         /**
46          * アプリケーション設定ファイルの名前
47          */
48         private static final String CONFIG_NAME = "appConfig.xml";
49
50         /**
51          * 全ユーザー用キャラクターディレクトリのシステムプロパティのキー名.<br>
52          */
53         public static final String COMMON_CHARACTER_DIR_PROPERTY_NAME = "character.dir";
54
55         /**
56          * 開発用仕様バージョン番号
57          */
58         private static final String DEFAULT_SPECIFICATION_VERSION = "1.0";
59
60
61         /**
62          * ロガー
63          */
64         private static final Logger logger = Logger.getLogger(AppConfig.class.getName());
65
66
67         /**
68          * シングルトンインスタンス
69          */
70         private static final AppConfig singleton = new AppConfig();
71
72         /**
73          * プロパティ変更リスナのサポート
74          */
75         private final PropertyChangeSupport propChangeSupport = new PropertyChangeSupport(this);
76
77
78         public void addPropertyChangeListener(PropertyChangeListener listener) {
79                 propChangeSupport.addPropertyChangeListener(listener);
80         }
81
82         public void removePropertyChangeListener(PropertyChangeListener listener) {
83                 propChangeSupport.removePropertyChangeListener(listener);
84         }
85
86         public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
87                 propChangeSupport.addPropertyChangeListener(propertyName, listener);
88         }
89
90         public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
91                 propChangeSupport.removePropertyChangeListener(propertyName, listener);
92         }
93
94         /**
95          * インスタンスを取得する.
96          *
97          * @return インスタンス
98          */
99         public static AppConfig getInstance() {
100                 return singleton;
101         }
102
103         /**
104          * プライベートコンストラクタ
105          */
106         private AppConfig() {
107                 loadAppVersions();
108         }
109
110         private String implementationVersion;
111
112         private String specificationVersion;
113
114         /**
115          * 実装バージョンを取得する.<br>
116          * ビルドされたjarパッケージからバージョン情報を取得する.<br>
117          * クラスパスの実行からのバージョンは常に「develop」となる.<br>
118          *
119          * @return 実装バージョン
120          */
121         public String getImplementationVersion() {
122                 return implementationVersion;
123         }
124
125         /**
126          * 仕様バージョンを取得する.<br>
127          * ビルドされたjarパッケージからバージョン情報を取得する.<br>
128          * クラスパスの実行からのバージョンは常に「develop」となる.<br>
129          *
130          * @return 仕様バージョン
131          */
132         public String getSpecificationVersion() {
133                 return specificationVersion;
134         }
135
136         /**
137          * ビルドされたjarパッケージからバージョン情報を取得する.<br>
138          * クラスパスの実行からのバージョンは常に「develop」となる.<br>
139          */
140         private void loadAppVersions() {
141                 Package pak = this.getClass().getPackage();
142                 String implementationVersion = "develop";
143                 String specificationVersion = DEFAULT_SPECIFICATION_VERSION;
144                 if (pak != null) {
145                         String vInfo = pak.getImplementationVersion();
146                         if (vInfo != null && implementationVersion.trim().length() > 0) {
147                                 implementationVersion = vInfo.trim();
148                         }
149                         String specVInfo = pak.getSpecificationVersion();
150                         if (specVInfo != null && specVInfo.trim().length() > 0) {
151                                 specificationVersion = specVInfo.trim();
152                         }
153                 }
154
155                 this.implementationVersion = implementationVersion;
156                 this.specificationVersion = specificationVersion;
157         }
158
159         /**
160          * 設定ファイルのロケール固有版へのファイル末尾の修飾文字列を読み込み順に取得する.
161          * @param locale ロケール、nullの場合はデフォルト
162          * @return ロケールを表すファイル末尾の修飾文字列の読み込み順のリスト
163          */
164         private String[] getLocalizedSuffix(Locale locale) {
165                 if (locale == null) {
166                         locale = Locale.getDefault();
167                 }
168
169                 String language = locale.getLanguage();
170                 String country = locale.getCountry();
171                 String variant = locale.getVariant();
172
173                 return new String[] {
174                         "",
175                         "_" + language,
176                         "_" + language + "_" + country,
177                         "_" + language + "_" + country + "_" + variant,
178                 };
179         }
180
181         /**
182          * 指定されたファイル名の拡張子の前にロケール固有の修飾文字列を付与したリスト作成して返す.
183          * @param base ファイル名
184          * @param locale ロケール、nullの場合はデフォルト
185          * @return ロケールの検索順序でのロケール固有の修飾文字列が付与されたファイルのリスト
186          */
187         private List<File> expandLocalizedSuffix(File base, Locale locale) {
188                 String path = base.getPath();
189
190                 int pt = path.lastIndexOf(".");
191                 String left, right;
192                 if (pt >= 0) {
193                         left = path.substring(0, pt);
194                         right = path.substring(pt);
195
196                 } else {
197                         left = path;
198                         right = "";
199                 }
200
201                 ArrayList<File> files = new ArrayList<File>();
202                 for (String suffix : getLocalizedSuffix(locale)) {
203                         String newPath = left + suffix + right;
204                         //System.out.println("newpath=" + newPath);
205                         files.add(new File(newPath));
206                 }
207                 return files;
208         }
209
210         /**
211          * 設定ファイルの読み込み順序で、読み込むべきURIのリストを返す.<br>
212          * <ul>
213          * <li>(1) リソース上の/appConfig.xml</li>
214          * <li>(2) appConfigFileシステムプロパティで指定されたファイル</li>
215          * <li>(3) コードベース下のappConfig.xml</li>
216          * <li>(4) アプリケーションデータ保存先のappConfig.xml</li>
217          * </ul>
218          * appConfigFileシステムプロパティがある場合は、(1)(2)の順。 <br>
219          * 指定がない場合は、(1)(3)(4)の順に読み取る.<br>
220          *
221          * @return 優先順位での設定ファイルの読み込み先URIのリスト
222          * @throws IOException
223          */
224         public List<URI> getCandidateURIs() throws IOException {
225                 List<URI> uris = new ArrayList<URI>();
226                 // リソース中の既定 (ロケール識別あり)
227                 getAppConfigResourceURI(uris);
228
229                 // システムプロパティでappConfig.xmlを明示している場合は、それを読み込む。
230                 // (appConfigFileシステムプロパティが空の場合は、リソース埋め込みの既定の設定だけをよみこむ)
231                 String specifiedAppConfig = System.getProperty("appConfigFile");
232                 if (specifiedAppConfig != null) {
233                         if (specifiedAppConfig.trim().length() > 0) {
234                                 File specifiedAppConfigFile = new File(specifiedAppConfig);
235                                 uris.add(specifiedAppConfigFile.toURI());
236                         }
237
238                 } else {
239                         // システムプロパティて明示していない場合は、まずコードベースを使用する.(ロケール識別あり)
240                         File codeBase = ConfigurationDirUtilities.getApplicationBaseDir();
241                         for (File localizedFile : expandLocalizedSuffix(new File(codeBase,
242                                         CONFIG_NAME).getCanonicalFile(), null)) {
243                                 uris.add(localizedFile.toURI());
244                         }
245
246                         // システムプロパティて明示していない場合は、次にユーザディレクトリを使用する.
247                         File userDataDir = ConfigurationDirUtilities.getUserDataDir();
248                         uris.add(new File(userDataDir, CONFIG_NAME).toURI());
249                 }
250                 return uris;
251         }
252
253         /**
254          * リソース上のAppConfigの読み込みURIを追記して返す
255          * @param uris
256          */
257         protected void getAppConfigResourceURI(List<URI> uris) {
258                 for (File localizedFile : expandLocalizedSuffix(new File(getClass()
259                                 .getResource("/" + CONFIG_NAME).getPath()), null)) {
260                         uris.add(localizedFile.toURI());
261                 }
262         }
263
264         /**
265          * 保存先の試行順序ごとのファイルのリスト。
266          *
267          * @return 保存先(優先順)
268          */
269         public List<File> getPrioritySaveFileList() {
270                 ArrayList<File> saveFiles = new ArrayList<File>();
271
272                 String specifiedAppConfig = System.getProperty("appConfigFile");
273                 if (specifiedAppConfig != null) {
274                         // システムプロパティでappConfig.xmlを明示している場合
275                         if (specifiedAppConfig.trim().length() > 0) {
276                                 File specifiedAppConfigFile = new File(specifiedAppConfig);
277                                 if (!specifiedAppConfigFile.exists()
278                                                 || specifiedAppConfigFile.canWrite()) {
279                                         // まだ存在しないか、書き込み可能である場合のみ候補とする.
280                                         saveFiles.add(specifiedAppConfigFile);
281                                 }
282                         }
283                 } else {
284                         // システムプロパティappConfigFileがなければユーザディレクトリへ書き込む
285                         // ユーザディレクトリは常に候補とする.
286                         File userDataDir = ConfigurationDirUtilities.getUserDataDir();
287                         saveFiles.add(new File(userDataDir, CONFIG_NAME));
288                 }
289
290                 return saveFiles;
291         }
292
293         /**
294          * プロパティをロードする.<br>
295          * 存在しないか、読み取りに失敗した場合は、該当ファイルはスキップされる.<br>
296          */
297         public void loadConfig() {
298                 try {
299                         loadConfig(getCandidateURIs());
300                 } catch (IOException ex) {
301                         throw new RuntimeException("appConfig.xml loading failed.", ex);
302                 }
303         }
304
305         /**
306          * プロパティをロードする.<br>
307          * 存在しないか、読み取りに失敗した場合は、該当ファイルはスキップされる.<br>
308          * @param uris パラメーターの読み込み順
309          */
310         public void loadConfig(List<URI> uris) {
311                 if (uris == null) {
312                         uris = Collections.emptyList();
313                 }
314                 Properties config = new Properties();
315                 try {
316                         for (URI uri : uris) {
317                                 if (uri == null) {
318                                         continue; // リソースがない場合はnullになる
319                                 }
320                                 // ファイルの実在チェック (チェックできる場合のみ)
321                                 if ("file".equals(uri.getScheme())) {
322                                         File file = new File(uri);
323                                         if (!file.exists()) {
324                                                 logger.log(Level.CONFIG, "appConfig.xml is not found.:" + file);
325                                                 continue;
326                                         }
327                                 }
328                                 // appConfig.xmlの読み込みを行う.
329                                 // Properties#loadFromXML() はXMLからキーを読み取り、既存のキーに対して上書きする.
330                                 // XMLに存在しないキーは読み込み前のままなので、繰り返し呼び出すことで「重ね合わせ」することができる.
331                                 try {
332                                         URL resourceURL = uri.toURL();
333                                         InputStream is = resourceURL.openStream();
334                                         try {
335                                                 config.loadFromXML(is);
336                                                 logger.log(Level.CONFIG, "appConfig.xml is loaded.:" + uri);
337                                         } finally {
338                                                 is.close();
339                                         }
340
341                                 } catch (FileNotFoundException ex) {
342                                         logger.log(Level.CONFIG, "appConfig.xml is not found.: " + uri, ex);
343                                         // 無視する (無い場合は十分にありえるので「情報」レベルでログ。)
344                                 } catch (Exception ex) {
345                                         logger.log(Level.WARNING, "appConfig.xml loading failed.: " + uri, ex);
346                                         // 無視する
347                                 }
348                         }
349
350                 } catch (RuntimeException ex) {
351                         throw new RuntimeException("appConfig.xml loading failed.", ex);
352                 }
353                 BeanPropertiesUtilities.loadFromProperties(this, config);
354         }
355
356         /**
357          * プロパティをアプリケーションデータの指定した保存先に保存する.
358          *
359          * @throws IOException
360          *             保存に失敗した場合
361          */
362         public void saveConfig(List<File> prioritySaveFiles) throws IOException {
363                 Properties config = getProperties();
364                 IOException oex = null;
365                 for (File configStore : prioritySaveFiles) {
366                         try {
367                                 OutputStream os = new BufferedOutputStream(
368                                                 new FileOutputStream(configStore));
369                                 try {
370                                         config.storeToXML(os, CONFIG_NAME, "UTF-8");
371                                         return; // 成功した時点で終了
372
373                                 } finally {
374                                         os.close();
375                                 }
376
377                         } catch (IOException ex) {
378                                 logger.log(Level.WARNING, "アプリケーション設定の保存に失敗しました" + ex, ex);
379                                 oex = ex;
380                         }
381                 }
382
383                 // 例外が発生していれば、最後の例外を返す.
384                 if (oex != null) {
385                         throw oex;
386                 }
387         }
388
389         /**
390          * プロパティをアプリケーションデータの保存先に保存する.
391          *
392          * @throws IOException
393          *             保存に失敗した場合
394          */
395         public void saveConfig() throws IOException {
396                 saveConfig(getPrioritySaveFileList());
397         }
398
399         /**
400          * アプリケーション設定値のデフォルト値(リソース上のAppConfigのみ)を取得する
401          * @return
402          */
403         public static Map<String, Object> getDefaultProperties() {
404                 PropertyAccessorMap<AppConfig> accessorMap = BeanPropertiesUtilities.getPropertyAccessorMap(AppConfig.class);
405
406                 AppConfig dummy = new AppConfig(); // アプリケーションから参照されないダミーのインスタンスを作成する.
407
408                 // リソース上のAppConfigのみ読み込み
409                 List<URI> uris = new ArrayList<URI>();
410                 dummy.getAppConfigResourceURI(uris);
411                 dummy.loadConfig(uris);
412
413                 accessorMap.setBean(dummy);
414
415                 // 読み込んだプロパティの書き出し
416                 Map<String, Object> defMap = new HashMap<String, Object>();
417                 for (Map.Entry<String, PropertyAccessor> propEntry : accessorMap.entrySet()) {
418                         String name = propEntry.getKey();
419                         PropertyAccessor accessor = propEntry.getValue();
420                         Object value = accessor.getValue();
421                         defMap.put(name,  value);
422                 }
423                 return defMap;
424         }
425
426         /**
427          * Propertiesの値を設定した場合に設定できない項目があるかチェックする.<br>
428          * このメソッドを呼び出しても、アプリケーション設定自身は何も影響されない.<br>
429          *
430          * @param props
431          *            適用するプロパティ
432          * @return 設定できなかったプロパティキーのコレクション、問題なければ空が返される.
433          */
434         public static Set<String> checkProperties(Map<String, Object> props) {
435                 if (props == null) {
436                         throw new IllegalArgumentException();
437                 }
438                 AppConfig dummy = new AppConfig(); // アプリケーションから参照されないダミーのインスタンスを作成する.
439                 return update(props, dummy);
440         }
441
442
443         /**
444          * Propertiesの値で設定を更新する.<br>
445          *
446          * @param props
447          *            適用するプロパティ
448          * @return 設定できなかったプロパティキーのコレクション、問題なければ空が返される.
449          */
450         public Set<String> update(Map<String, Object> props) {
451                 return update(props, this);
452         }
453
454         /**
455          * Propertiesの値で設定を更新する.<br>
456          *
457          * @param props
458          *            適用するプロパティ
459          * @param bean
460          *            適用するAppConfigのインスタンス(ドライランと本番の切り替え用)
461          * @return 設定できなかったプロパティキーのコレクション、問題なければ空が返される.
462          */
463         private static Set<String> update(Map<String, Object>  props, AppConfig bean) {
464                 if (props == null) {
465                         throw new IllegalArgumentException();
466                 }
467                 HashSet<String> rejectedNames = new HashSet<String>();
468
469                 PropertyAccessorMap<AppConfig> accessorMap = BeanPropertiesUtilities.getPropertyAccessorMap(AppConfig.class);
470                 accessorMap.setBean(bean);
471
472                 for (Map.Entry<String, Object> propEntry : props.entrySet()) {
473                         String name = propEntry.getKey();
474                         Object value = propEntry.getValue();
475
476                         PropertyAccessor accessor = accessorMap.get(name);
477                         if (accessor == null) {
478                                 // プロパティがない
479                                 rejectedNames.add(name);
480                                 continue;
481                         }
482
483                         Class<?> propertyType = accessor.getPropertyType();
484                         if (propertyType.isPrimitive() && value == null) {
485                                 // プリミティブ型なのにnullは入れられない
486                                 rejectedNames.add(name);
487                                 continue;
488                         }
489
490                         try {
491                                 accessor.setValue(value);
492
493                         } catch (Exception ex) {
494                                 // 何らかの理由でプロパティの設定に失敗している場合
495                                 rejectedNames.add(name);
496                                 logger.log(Level.WARNING, "invalid propery: " + name + " /val=" + value, ex);
497                                 continue;
498                         }
499                 }
500                 return rejectedNames;
501         }
502
503         /**
504          * このアプリケーション設定をプロパティに書き出して返します.<br>
505          *
506          * @return プロパティ
507          */
508         public Properties getProperties() {
509                 Properties config = new Properties();
510                 BeanPropertiesUtilities.saveToProperties(this, config);
511                 return config;
512         }
513
514
515         /**
516          * プロファイル選択ダイアログのプロファイルのサンプルイメージの背景色
517          *
518          * @return サンプルイメージの背景色
519          */
520         public Color getSampleImageBgColor() {
521                 return sampleImageBgColor;
522         }
523
524         public static final String SAMPLE_IMAGE_BG_COLOR = "sampleImageBgColor";
525
526         public void setSampleImageBgColor(Color sampleImageBgColor) {
527                 if (sampleImageBgColor == null) {
528                         throw new IllegalArgumentException();
529                 }
530                 Color old = this.sampleImageBgColor;
531                 if (old == null ? sampleImageBgColor != null : !old.equals(sampleImageBgColor)) {
532                         this.sampleImageBgColor = sampleImageBgColor;
533                         propChangeSupport.firePropertyChange(SAMPLE_IMAGE_BG_COLOR, old, sampleImageBgColor);
534                 }
535         }
536
537         private Color sampleImageBgColor = Color.white;
538
539
540         /**
541          * デフォルトのイメージ背景色を取得する.
542          *
543          * @return デフォルトのイメージ背景色
544          */
545         public Color getDefaultImageBgColor() {
546                 return defaultImageBgColor;
547         }
548
549         public static final String DEFAULT_IMAGE_BG_COLOR = "defaultImageBgColor";
550
551         public void setDefaultImageBgColor(Color defaultImageBgColor) {
552                 if (defaultImageBgColor == null) {
553                         throw new IllegalArgumentException();
554                 }
555                 Color old = this.defaultImageBgColor;
556                 if (old == null ? defaultImageBgColor != null : !old.equals(defaultImageBgColor)) {
557                         this.defaultImageBgColor = defaultImageBgColor;
558                         propChangeSupport.firePropertyChange(DEFAULT_IMAGE_BG_COLOR, old, defaultImageBgColor);
559                 }
560         }
561
562         private Color defaultImageBgColor = Color.white;
563
564         /**
565          * 使用中アイテムの背景色を取得する.
566          *
567          * @return 使用中アイテムの背景色
568          */
569         public Color getCheckedItemBgColor() {
570                 return checkedItemBgColor;
571         }
572
573         public static final String CHECKED_ITEM_BG_COLOR = "checkedItemBgColor";
574
575         public void setCheckedItemBgColor(Color checkedItemBgColor) {
576                 if (checkedItemBgColor == null) {
577                         throw new IllegalArgumentException();
578                 }
579                 Color old = this.checkedItemBgColor;
580                 if (old == null ? checkedItemBgColor != null : !old.equals(checkedItemBgColor)) {
581                         this.checkedItemBgColor = checkedItemBgColor;
582                         propChangeSupport.firePropertyChange(CHECKED_ITEM_BG_COLOR, old, checkedItemBgColor);
583                 }
584         }
585
586         private Color checkedItemBgColor = Color.cyan.brighter();
587
588
589         /**
590          *  選択アイテムの背景色を取得する
591          *
592          * @return 選択アイテムの背景色
593          */
594         public Color getSelectedItemBgColor() {
595                 return selectedItemBgColor;
596         }
597
598         public static final String SELECTED_ITEM_BG_COLOR = "selectedItemBgColor";
599
600         public void setSelectedItemBgColor(Color selectedItemBgColor) {
601                 if (selectedItemBgColor == null) {
602                         throw new IllegalArgumentException();
603                 }
604                 Color old = this.selectedItemBgColor;
605                 if (old == null ? selectedItemBgColor != null : !old.equals(selectedItemBgColor)) {
606                         this.selectedItemBgColor = selectedItemBgColor;
607                         propChangeSupport.firePropertyChange(SELECTED_ITEM_BG_COLOR, old, selectedItemBgColor);
608                 }
609         }
610
611         private Color selectedItemBgColor = Color.orange;
612
613         /**
614          * 不備のあるデータ行の背景色を取得する.
615          *
616          * @return 不備のあるデータ行の背景色
617          */
618         public Color getInvalidBgColor() {
619                 return invalidBgColor;
620         }
621
622         public static final String INVALID_BG_COLOR = "invalidBgColor";
623
624         public void setInvalidBgColor(Color invalidBgColor) {
625                 if (invalidBgColor == null) {
626                         throw new IllegalArgumentException();
627                 }
628                 Color old = this.invalidBgColor;
629                 if (old == null ? invalidBgColor != null : !old.equals(invalidBgColor)) {
630                         this.invalidBgColor = invalidBgColor;
631                         propChangeSupport.firePropertyChange(INVALID_BG_COLOR, old, invalidBgColor);
632                 }
633         }
634
635         private Color invalidBgColor = Color.red.brighter().brighter();
636
637         /**
638          * JPEG画像変換時の圧縮率を取得する.
639          *
640          * @return 圧縮率
641          */
642         public float getCompressionQuality() {
643                 return compressionQuality;
644         }
645
646         public static final String COMPRESSION_QUALITY = "compressionQuality";
647
648         public void setCompressionQuality(float compressionQuality) {
649                 if (compressionQuality < .1f || compressionQuality > 1f) {
650                         throw new IllegalArgumentException();
651                 }
652                 float old = this.compressionQuality;
653                 if (old != compressionQuality) {
654                         this.compressionQuality = compressionQuality;
655                         propChangeSupport.firePropertyChange(COMPRESSION_QUALITY, old, compressionQuality);
656                 }
657         }
658
659         private float compressionQuality = .8f;
660
661         /**
662          * エクスポートウィザードのプリセットにパーツ不足時の警告色(前景色)を取得する.
663          *
664          * @return エクスポートウィザードのプリセットにパーツ不足時の警告色(前景色)
665          */
666         public Color getExportPresetWarningsForegroundColor() {
667                 return exportPresetWarningsForegroundColor;
668         }
669
670         public static final String EXPORT_PRESET_WARNINGS_FOREGROUND_COLOR = "exportPresetWarningsForegroundColor";
671
672         public void setExportPresetWarningsForegroundColor(Color exportPresetWarningsForegroundColor) {
673                 if (exportPresetWarningsForegroundColor == null) {
674                         throw new IllegalArgumentException();
675                 }
676                 Color old = this.exportPresetWarningsForegroundColor;
677                 if (old == null ? exportPresetWarningsForegroundColor != null
678                                 : !old.equals(exportPresetWarningsForegroundColor)) {
679                         this.exportPresetWarningsForegroundColor = exportPresetWarningsForegroundColor;
680                         propChangeSupport.firePropertyChange(EXPORT_PRESET_WARNINGS_FOREGROUND_COLOR, old,
681                                         exportPresetWarningsForegroundColor);
682                 }
683         }
684
685         private Color exportPresetWarningsForegroundColor = Color.red;
686
687         /**
688          * JARファイル転送用バッファサイズ.<br>
689          *
690          * @return JARファイル転送用バッファサイズ.
691          */
692         public int getJarTransferBufferSize() {
693                 return jarTransferBufferSize;
694         }
695
696         public static final String JAR_TRANSFER_BUFFER_SIZE = "jarTransferBufferSize";
697
698         public void setJarTransferBufferSize(int jarTransferBufferSize) {
699                 if (jarTransferBufferSize <= 0) {
700                         throw new IllegalArgumentException();
701                 }
702                 int old = this.jarTransferBufferSize;
703                 if (old != jarTransferBufferSize) {
704                         this.jarTransferBufferSize = jarTransferBufferSize;
705                         propChangeSupport.firePropertyChange(JAR_TRANSFER_BUFFER_SIZE, old, jarTransferBufferSize);
706                 }
707         }
708
709         private int jarTransferBufferSize = 4096;
710
711         /**
712          * ZIPファイル名のエンコーディング.<br>
713          *
714          * @return ZIPファイル名のエンコーディング.<br>
715          */
716         public String getZipNameEncoding() {
717                 return zipNameEncoding;
718         }
719
720         public static final String ZIP_NAME_ENCODING = "zipNameEncoding";
721
722         public void setZipNameEncoding(String zipNameEncoding) {
723                 if (zipNameEncoding == null) {
724                         throw new IllegalArgumentException();
725                 }
726                 try {
727                         Charset.forName(zipNameEncoding);
728                 } catch (Exception ex) {
729                         throw new RuntimeException("unsupported charset: " + zipNameEncoding);
730                 }
731                 String old = this.zipNameEncoding;
732                 if (old == null ? zipNameEncoding != null : !old.equals(zipNameEncoding)) {
733                         this.zipNameEncoding = zipNameEncoding;
734                         propChangeSupport.firePropertyChange(ZIP_NAME_ENCODING, old, zipNameEncoding);
735                 }
736         }
737
738         private String zipNameEncoding = "csWindows31J";
739
740         /**
741          * ディセーブルなテーブルのセルのフォアグラウンドカラーを取得する.
742          *
743          * @return ディセーブルなテーブルのセルのフォアグラウンドカラー
744          */
745         public Color getDisabledCellForgroundColor() {
746                 return disabledCellForegroundColor;
747         }
748
749         public static final String DISABLED_CELL_FOREGROUND_COLOR = "disabledCellForegroundColor";
750
751         public void setDisabledCellForegroundColor(Color disabledCellForegroundColor) {
752                 if (disabledCellForegroundColor == null) {
753                         throw new IllegalArgumentException();
754                 }
755                 Color old = this.disabledCellForegroundColor;
756                 if (old == null ? disabledCellForegroundColor != null : !old.equals(disabledCellForegroundColor)) {
757                         this.disabledCellForegroundColor = disabledCellForegroundColor;
758                         propChangeSupport.firePropertyChange(DISABLED_CELL_FOREGROUND_COLOR, old, disabledCellForegroundColor);
759                 }
760         }
761
762         private Color disabledCellForegroundColor = Color.gray;
763
764
765         /**
766          * ディレクトリを監視する間隔(mSec)を取得する.
767          *
768          * @return ディレクトリを監視する間隔(mSec)
769          */
770         public int getDirWatchInterval() {
771                 return dirWatchInterval;
772         }
773
774         public static final String DIR_WATCH_INTERVAL = "dirWatchInterval";
775
776         public void setDirWatchInterval(int dirWatchInterval) {
777                 if (dirWatchInterval <= 0) {
778                         throw new IllegalArgumentException();
779                 }
780                 int old = this.dirWatchInterval;
781                 if (old != dirWatchInterval) {
782                         this.dirWatchInterval = dirWatchInterval;
783                         propChangeSupport.firePropertyChange(DIR_WATCH_INTERVAL, old, dirWatchInterval);
784                 }
785         }
786
787         private int dirWatchInterval = 7 * 1000;
788
789         /**
790          * ディレクトリの監視を有効にするか?
791          *
792          * @return ディレクトリの監視を有効にする場合はtrue
793          */
794         public boolean isEnableDirWatch() {
795                 return enableDirWatch;
796         }
797
798         public static final String ENABLE_DIR_WATCH = "enableDirWatch";
799
800         public void setEnableDirWatch(boolean enableDirWatch) {
801                 boolean old = this.enableDirWatch;
802                 if (old != enableDirWatch) {
803                         this.enableDirWatch = enableDirWatch;
804                         propChangeSupport.firePropertyChange(ENABLE_DIR_WATCH, old, enableDirWatch);
805                 }
806         }
807
808         private boolean enableDirWatch = true;
809
810         /**
811          * ファイル転送に使うバッファサイズ.<br>
812          *
813          * @return バッファサイズ
814          */
815         public int getFileTransferBufferSize() {
816                 return fileTransferBufferSize;
817         }
818
819         public static final String FILE_TRANSFER_BUFFER_SIZE = "fileTransferBufferSize";
820
821         public void setFileTransferBufferSize(int fileTransferBufferSize) {
822                 if (fileTransferBufferSize <= 0) {
823                         throw new IllegalArgumentException();
824                 }
825                 int old = this.fileTransferBufferSize;
826                 if (old != fileTransferBufferSize) {
827                         this.fileTransferBufferSize = fileTransferBufferSize;
828                         propChangeSupport.firePropertyChange(FILE_TRANSFER_BUFFER_SIZE, old, fileTransferBufferSize);
829                 }
830         }
831
832         private int fileTransferBufferSize = 4096;
833
834         /**
835          * プレビューのインジケータを表示するまでのディレイ(mSec)を取得する.
836          *
837          * @return プレビューのインジケータを表示するまでのディレイ(mSec)
838          */
839         public long getPreviewIndicatorDelay() {
840                 return previewIndeicatorDelay;
841         }
842
843         public static final String PREVIEW_INDEICATOR_DELAY = "previewIndeicatorDelay";
844
845         public void setPreviewIndeicatorDelay(long previewIndeicatorDelay) {
846                 if (previewIndeicatorDelay < 0) {
847                         throw new IllegalArgumentException();
848                 }
849                 long old = this.previewIndeicatorDelay;
850                 if (old != previewIndeicatorDelay) {
851                         this.previewIndeicatorDelay = previewIndeicatorDelay;
852                         propChangeSupport.firePropertyChange(PREVIEW_INDEICATOR_DELAY, old, previewIndeicatorDelay);
853                 }
854         }
855
856         private long previewIndeicatorDelay = 300;
857
858         /**
859          * 情報ダイアログの編集ボタンを「開く」アクションにする場合はtrue、「編集」アクションにする場合はfalse
860          *
861          * @return trueならばOpen、falseならばEdit
862          */
863         public boolean isInformationDialogOpenMethod() {
864                 return informationDialogOpenMethod;
865         }
866
867         public static final String INFORMATION_DIALOG_OPEN_METHOD = "informationDialogOpenMethod";
868
869         public void setInformationDialogOpenMethod(
870                         boolean informationDialogOpenMethod) {
871                 boolean old = this.informationDialogOpenMethod;
872                 if (old != informationDialogOpenMethod) {
873                         this.informationDialogOpenMethod = informationDialogOpenMethod;
874                         propChangeSupport.firePropertyChange("informationDialogOpenMethod", old, informationDialogOpenMethod);
875                 }
876         }
877
878         private boolean informationDialogOpenMethod = true;
879
880         /**
881          * ログを常に残すか?<br>
882          * falseの場合は{@link ApplicationLogHandler}の実装に従って終了時に 必要なければログは削除される.<br>
883          *
884          * @return 常に残す場合はtrue、そうでなければfalse
885          */
886         public boolean isNoRemoveLog() {
887                 return noRemoveLog;
888         }
889
890         public static final String NO_REMOVE_LOG = "noRemoveLog";
891
892         public void setNoRemoveLog(boolean noRemoveLog) {
893                 boolean old = this.noRemoveLog;
894                 if (old != noRemoveLog) {
895                         this.noRemoveLog = noRemoveLog;
896                         propChangeSupport.firePropertyChange(NO_REMOVE_LOG, old, noRemoveLog);
897                 }
898         }
899
900         private boolean noRemoveLog = false;
901
902
903         /**
904          * テーブルのグリッド色.<br>
905          *
906          * @return テーブルのグリッド色
907          */
908         public Color getGridColor() {
909                 return gridColor;
910         }
911
912         public static final String GRID_COLOR = "gridColor";
913
914         public void setGridColor(Color gridColor) {
915                 if (gridColor == null) {
916                         throw new IllegalArgumentException();
917                 }
918                 Color old = this.gridColor;
919                 if (old == null ? gridColor != null : !old.equals(gridColor)) {
920                         this.gridColor = gridColor;
921                         propChangeSupport.firePropertyChange(GRID_COLOR, old, gridColor);
922                 }
923         }
924
925         private Color gridColor = Color.gray;
926
927         /**
928          * カラーダイアログの値が変更されたら、自動的にプレビューを更新するか?
929          *
930          * @return カラーダイアログの値が変更されたら、自動的にプレビューを更新する場合はtrue (デフォルトはtrue)
931          */
932         public boolean isEnableAutoColorChange() {
933                 return enableAutoColorChange;
934         }
935
936         public static final String ENABLE_AUTO_COLOR_CHANGE = "enableAutoColorChange";
937
938         public void setEnableAutoColorChange(boolean enableAutoColorChange) {
939                 boolean old = this.enableAutoColorChange;
940                 if (old != enableAutoColorChange) {
941                         this.enableAutoColorChange = enableAutoColorChange;
942                         propChangeSupport.firePropertyChange(ENABLE_AUTO_COLOR_CHANGE, old, enableAutoColorChange);
943                 }
944         }
945
946         private boolean enableAutoColorChange = true;
947
948         public static final String AUTHOR_EDIT_CONFLICT_BG_COLOR = "authorEditConflictBgColor";
949
950         public void setAuthorEditConflictBgColor(Color authorEditConflictBgColor) {
951                 if (authorEditConflictBgColor == null) {
952                         throw new IllegalArgumentException();
953                 }
954                 Color old = this.authorEditConflictBgColor;
955                 if (old == null ? authorEditConflictBgColor != null : !old.equals(authorEditConflictBgColor)) {
956                         this.authorEditConflictBgColor = authorEditConflictBgColor;
957                         propChangeSupport.firePropertyChange(AUTHOR_EDIT_CONFLICT_BG_COLOR, old, authorEditConflictBgColor);
958                 }
959         }
960
961         /**
962          * パーツの作者編集時に複数作者を選択した場合のに入力ボックスの背景色
963          *
964          * @return 背景色
965          */
966         public Color getAuthorEditConflictBgColor() {
967                 return authorEditConflictBgColor;
968         }
969
970         Color authorEditConflictBgColor = Color.yellow;
971
972
973         public static final String MAIN_FRAME_MAX_WIDTH = "mainFrameMaxWidth";
974
975         public void setMainFrameMaxWidth(int mainFrameMaxWidth) {
976                 int old = mainFrameMaxWidth;
977                 if (old != mainFrameMaxWidth) {
978                         this.mainFrameMaxWidth = mainFrameMaxWidth;
979                         propChangeSupport.firePropertyChange(MAIN_FRAME_MAX_WIDTH, old, mainFrameMaxWidth);
980                 }
981         }
982
983         /**
984          * メインフレームの初期表示時の最大幅
985          *
986          * @return メインフレームの初期表示時の最大幅
987          */
988         public int getMainFrameMaxWidth() {
989                 return mainFrameMaxWidth;
990         }
991
992         private int mainFrameMaxWidth = 800;
993
994         public static final String MAIN_FRAME_MAX_HEIGHT = "mainFrameMaxHeight";
995
996         public void setMainFrameMaxHeight(int mainFrameMaxHeight) {
997                 int old = this.mainFrameMaxHeight;
998                 if (old != mainFrameMaxHeight) {
999                         this.mainFrameMaxHeight = mainFrameMaxHeight;
1000                         propChangeSupport.firePropertyChange(MAIN_FRAME_MAX_HEIGHT, old, mainFrameMaxHeight);
1001                 }
1002         }
1003
1004         /**
1005          * メインフレームの初期表示時の最大高さ
1006          *
1007          * @return メインフレームの初期表示時の最大高さ
1008          */
1009         public int getMainFrameMaxHeight() {
1010                 return mainFrameMaxHeight;
1011         }
1012
1013         private int mainFrameMaxHeight = 600;
1014
1015
1016         /**
1017          * カラーダイアログで存在しないレイヤーをディセーブルにしない.
1018          *
1019          * @return ディセーブルにしない場合はtrue
1020          */
1021         public boolean isNotDisableLayerTab() {
1022                 return notDisableLayerTab;
1023         }
1024
1025         public static final String NOT_DISABLE_LAYER_TAB = "notDisableLayerTab";
1026
1027         public void setNotDisableLayerTab(boolean notDisableLayerTab) {
1028                 boolean old = this.notDisableLayerTab;
1029                 if (old != notDisableLayerTab) {
1030                         this.notDisableLayerTab = notDisableLayerTab;
1031                         propChangeSupport.firePropertyChange(NOT_DISABLE_LAYER_TAB, old, notDisableLayerTab);
1032                 }
1033         }
1034
1035         private boolean notDisableLayerTab;
1036
1037
1038         /**
1039          * ログを消去する日数.<br>
1040          * この指定日を経過した古いログは削除される.<br>
1041          * 0の場合は削除されない.
1042          *
1043          * @return
1044          */
1045         public long getPurgeLogDays() {
1046                 return purgeLogDays;
1047         }
1048
1049         public static final String PURGE_LOG_DAYS = "purgeLogDays";
1050
1051         public void setPurgeLogDays(long purgeLogDays) {
1052                 long old = this.purgeLogDays;
1053                 if (old != purgeLogDays) {
1054                         this.purgeLogDays = purgeLogDays;
1055                         propChangeSupport.firePropertyChange(PURGE_LOG_DAYS, old, purgeLogDays);
1056                 }
1057         }
1058
1059         private long purgeLogDays = 10;
1060
1061         public String getPartsColorGroupPattern() {
1062                 return partsColorGroupPattern;
1063         }
1064
1065         public static final String PARTS_COLOR_GROUP_PATTERN = "partsColorGroupPattern";
1066
1067         public void setPartsColorGroupPattern(String pattern) {
1068                 if (pattern != null && pattern.trim().length() > 0) {
1069                         Pattern.compile(pattern);
1070                 }
1071                 String old = this.partsColorGroupPattern;
1072                 if (old == null ? pattern != null : !old.equals(pattern)) {
1073                         this.partsColorGroupPattern = pattern;
1074                         propChangeSupport.firePropertyChange(PARTS_COLOR_GROUP_PATTERN, old, pattern);
1075                 }
1076         }
1077
1078         private String partsColorGroupPattern = "^.*\\(@\\).*$";
1079
1080         private Color selectPanelTitleColor = Color.BLUE;
1081
1082         public Color getSelectPanelTitleColor() {
1083                 return selectPanelTitleColor;
1084         }
1085
1086         public static final String SELECT_PANEL_TITLE_COLOR = "selectPanelTitleColor";
1087
1088         public void setSelectPanelTitleColor(Color selectPanelTitleColor) {
1089                 if (selectPanelTitleColor == null) {
1090                         throw new IllegalArgumentException();
1091                 }
1092                 Color old = this.selectPanelTitleColor;
1093                 if (old == null ? selectPanelTitleColor != null : !old.equals(selectPanelTitleColor)) {
1094                         this.selectPanelTitleColor = selectPanelTitleColor;
1095                         propChangeSupport.firePropertyChange(SELECT_PANEL_TITLE_COLOR, old, selectPanelTitleColor);
1096                 }
1097         }
1098
1099         private boolean enableAutoShrinkPanel;
1100
1101         public boolean isEnableAutoShrinkPanel() {
1102                 return enableAutoShrinkPanel;
1103         }
1104
1105         public static final String ENABLE_AUTO_SHRINK_PANEL = "enableAutoShrinkPanel";
1106
1107         public void setEnableAutoShrinkPanel(boolean enableAutoShrinkPanel) {
1108                 boolean old = this.enableAutoShrinkPanel;
1109                 if (old != enableAutoShrinkPanel) {
1110                         this.enableAutoShrinkPanel = enableAutoShrinkPanel;
1111                         propChangeSupport.firePropertyChange(ENABLE_AUTO_SHRINK_PANEL, old, enableAutoShrinkPanel);
1112                 }
1113         }
1114
1115         public boolean isDisableWatchDirIfNotWritable() {
1116                 return disableWatchDirIfNotWritable;
1117         }
1118
1119         public static final String DISABLE_WATCH_DIR_IF_NOT_WRITABLE = "disableWatchDirIfNotWritable";
1120
1121         public void setDisableWatchDirIfNotWritable(boolean disableWatchDirIfNotWritable) {
1122                 boolean old = this.disableWatchDirIfNotWritable;
1123                 if (old != disableWatchDirIfNotWritable) {
1124                         this.disableWatchDirIfNotWritable = disableWatchDirIfNotWritable;
1125                         propChangeSupport.firePropertyChange(DISABLE_WATCH_DIR_IF_NOT_WRITABLE, old, disableWatchDirIfNotWritable);
1126                 }
1127         }
1128
1129         private boolean disableWatchDirIfNotWritable = true;
1130
1131         public static final String ENABLE_PNG_SUPPORT_FOR_WINDOWS = "enablePNGSupportForWindows";
1132
1133         public void setEnablePNGSupportForWindows(boolean enablePNGSupportForWindows) {
1134                 boolean old = this.enablePNGSupportForWindows;
1135                 if (old != enablePNGSupportForWindows) {
1136                         this.enablePNGSupportForWindows = enablePNGSupportForWindows;
1137                         propChangeSupport.firePropertyChange(ENABLE_PNG_SUPPORT_FOR_WINDOWS, old, enablePNGSupportForWindows);
1138                 }
1139         }
1140
1141         public boolean isEnablePNGSupportForWindows() {
1142                 return enablePNGSupportForWindows;
1143         }
1144
1145         private boolean enablePNGSupportForWindows = true;
1146
1147         /**
1148          * 画像表示(通常モード)でオプティマイズを有効にする最大倍率.
1149          */
1150         private double renderingOptimizeThresholdForNormal = 2.;
1151
1152         public static final String RENDERING_OPTIMIZE_THRESHOLD_FOR_NORMAL = "renderingOptimizeThresholdForNormal";
1153
1154         public void setRenderingOptimizeThresholdForNormal(
1155                         double renderingOptimizeThresholdForNormal) {
1156                 double old = this.renderingOptimizeThresholdForNormal;
1157                 if (old != renderingOptimizeThresholdForNormal) {
1158                         this.renderingOptimizeThresholdForNormal = renderingOptimizeThresholdForNormal;
1159                         propChangeSupport.firePropertyChange(RENDERING_OPTIMIZE_THRESHOLD_FOR_NORMAL, old,
1160                                         renderingOptimizeThresholdForNormal);
1161                 }
1162         }
1163
1164         public double getRenderingOptimizeThresholdForNormal() {
1165                 return renderingOptimizeThresholdForNormal;
1166         }
1167         /**
1168          * 画像表示(チェックモード)でオプティマイズを有効にする最大倍率.
1169          */
1170         private double renderingOptimizeThresholdForCheck = 0.;
1171
1172         public static final String RENDERING_OPTIMIZE_THRESHOLD_FOR_CHECK = "renderingOptimizeThresholdForCheck";
1173
1174         public void setRenderingOptimizeThresholdForCheck(
1175                         double renderingOptimizeThresholdForCheck) {
1176                 double old = this.renderingOptimizeThresholdForCheck;
1177                 if (old != renderingOptimizeThresholdForCheck) {
1178                         this.renderingOptimizeThresholdForCheck = renderingOptimizeThresholdForCheck;
1179                         propChangeSupport.firePropertyChange(RENDERING_OPTIMIZE_THRESHOLD_FOR_CHECK, old,
1180                                         renderingOptimizeThresholdForCheck);
1181                 }
1182         }
1183
1184         public double getRenderingOptimizeThresholdForCheck() {
1185                 return renderingOptimizeThresholdForCheck;
1186         }
1187
1188         /**
1189          * バイキュービックをサポートする場合
1190          */
1191         private boolean enableInterpolationBicubic = true;
1192
1193         public static final String ENABLE_INTERPOLATION_BICUBIC = "enableInterpolationBicubic";
1194
1195         public void setEnableInterpolationBicubic(boolean enableInterpolationBicubic) {
1196                 boolean old = this.enableInterpolationBicubic;
1197                 if (old != enableInterpolationBicubic) {
1198                         this.enableInterpolationBicubic = enableInterpolationBicubic;
1199                         propChangeSupport.firePropertyChange(ENABLE_INTERPOLATION_BICUBIC, old, enableInterpolationBicubic);
1200                 }
1201         }
1202
1203         public boolean isEnableInterpolationBicubic() {
1204                 return enableInterpolationBicubic;
1205         }
1206
1207         /**
1208          * 事前定義済みの倍率候補.<br>
1209          */
1210         private String predefinedZoomRanges = "20, 50, 80, 100, 120, 150, 200, 300, 400, 800";
1211
1212         public String getPredefinedZoomRanges() {
1213                 return predefinedZoomRanges;
1214         }
1215
1216         public static final String PREDEFINED_ZOOM_RANGES = "predefinedZoomRanges";
1217
1218         public void setPredefinedZoomRanges(String predefinedZoomRanges) {
1219                 if (predefinedZoomRanges == null) {
1220                         throw new IllegalArgumentException();
1221                 }
1222                 String old = this.predefinedZoomRanges;
1223                 if (old == null ? predefinedZoomRanges != null : !old.equals(predefinedZoomRanges)) {
1224                         this.predefinedZoomRanges = predefinedZoomRanges;
1225                         propChangeSupport.firePropertyChange(PREDEFINED_ZOOM_RANGES, old, predefinedZoomRanges);
1226                 }
1227         }
1228
1229         /**
1230          * ズームパネルを初期状態で表示するか?
1231          */
1232         private boolean enableZoomPanel = true;
1233
1234         public boolean isEnableZoomPanel() {
1235                 return enableZoomPanel;
1236         }
1237
1238         public static final String ENABLE_ZOOM_PANEL = "enableZoomPanel";
1239
1240         public void setEnableZoomPanel(boolean enableZoomPanel) {
1241                 boolean old = this.enableZoomPanel;
1242                 if (old != enableZoomPanel) {
1243                         this.enableZoomPanel = enableZoomPanel;
1244                         propChangeSupport.firePropertyChange(ENABLE_ZOOM_PANEL, old, enableZoomPanel);
1245                 }
1246         }
1247
1248         /**
1249          * ズームパネルをアクティブにする下部範囲
1250          */
1251         private int zoomPanelActivationArea = 30;
1252
1253         public int getZoomPanelActivationArea() {
1254                 return zoomPanelActivationArea;
1255         }
1256
1257         public static final String ZOOM_PANEL_ACTIVATION_AREA = "zoomPanelActivationArea";
1258
1259         public void setZoomPanelActivationArea(int zoomPanelActivationArea) {
1260                 int old = this.zoomPanelActivationArea;
1261                 if (old != zoomPanelActivationArea) {
1262                         this.zoomPanelActivationArea = zoomPanelActivationArea;
1263                         propChangeSupport.firePropertyChange(ZOOM_PANEL_ACTIVATION_AREA, old, zoomPanelActivationArea);
1264                 }
1265         }
1266
1267         /**
1268          * レンダリングヒントを使用するか?
1269          */
1270         private boolean enableRenderingHints = true;
1271
1272         public static final String ENABLE_RENDERING_HINTS = "enableRenderingHints";
1273
1274         public void setEnableRenderingHints(boolean enableRenderingHints) {
1275                 boolean old = this.enableRenderingHints;
1276                 if (old != enableRenderingHints) {
1277                         this.enableRenderingHints = enableRenderingHints;
1278                         propChangeSupport.firePropertyChange(ENABLE_RENDERING_HINTS, old, enableRenderingHints);
1279                 }
1280         }
1281
1282         public boolean isEnableRenderingHints() {
1283                 return enableRenderingHints;
1284         }
1285
1286         /**
1287          * グリッド描画とマスク
1288          */
1289         private int drawGridMask = 2;
1290
1291         public int getDrawGridMask() {
1292                 return drawGridMask;
1293         }
1294
1295         public static final String DRAW_GRID_MASK = "drawGridMask";
1296
1297         public void setDrawGridMask(int drawGridMask) {
1298                 drawGridMask &= 0x03;
1299                 int old = this.drawGridMask;
1300                 if (old != drawGridMask) {
1301                         this.drawGridMask = drawGridMask;
1302                         propChangeSupport.firePropertyChange(DRAW_GRID_MASK, old, drawGridMask);
1303                 }
1304         }
1305
1306         private Color previewGridColor = new Color(0x7f7f0000, true);
1307
1308         public Color getPreviewGridColor() {
1309                 return previewGridColor;
1310         }
1311
1312         public static final String PREVIEW_GRID_COLOR = "previewGridColor";
1313
1314         public void setPreviewGridColor(Color previewGridColor) {
1315                 Color old = this.previewGridColor;
1316                 if (old == null ? previewGridColor != null : !old.equals(previewGridColor)) {
1317                         this.previewGridColor = previewGridColor;
1318                         propChangeSupport.firePropertyChange(PREVIEW_GRID_COLOR, old, previewGridColor);
1319                 }
1320         }
1321
1322         private int previewGridSize = 20;
1323
1324         public int getPreviewGridSize() {
1325                 return previewGridSize;
1326         }
1327
1328         public static final String PREVIEW_GRID_SIZE = "previewGridSize";
1329
1330         public void setPreviewGridSize(int previewGridSize) {
1331                 int old = this.previewGridSize;
1332                 if (old != previewGridSize) {
1333                         this.previewGridSize = previewGridSize;
1334                         propChangeSupport.firePropertyChange(PREVIEW_GRID_SIZE, old, previewGridSize);
1335                 }
1336         }
1337
1338         /**
1339          * チェックモード時の余白サイズ(片側)
1340          */
1341         private int previewUnfilledSpaceForCheckMode = 0;
1342
1343         public int getPreviewUnfilledSpaceForCheckMode() {
1344                 return previewUnfilledSpaceForCheckMode;
1345         }
1346
1347         public static final String PREVIEW_UNFILLED_SPACE_FOR_CHECK_MODE = "previewUnfilledSpaceForCheckMode";
1348
1349         public void setPreviewUnfilledSpaceForCheckMode(int previewUnfilledSpaceForCheckMode) {
1350                 int old = this.previewUnfilledSpaceForCheckMode;
1351                 if (old != previewUnfilledSpaceForCheckMode) {
1352                         this.previewUnfilledSpaceForCheckMode = previewUnfilledSpaceForCheckMode;
1353                         propChangeSupport.firePropertyChange(PREVIEW_UNFILLED_SPACE_FOR_CHECK_MODE, old,
1354                                         previewUnfilledSpaceForCheckMode);
1355                 }
1356         }
1357
1358         /**
1359          * チェックモードでツールチップを表示するか?
1360          */
1361         private boolean enableCheckInfoTooltip = true;
1362
1363         public boolean isEnableCheckInfoTooltip() {
1364                 return enableCheckInfoTooltip;
1365         }
1366
1367         public static final String ENABLE_CHECK_INFO_TOOLTIP = "enableCheckInfoTooltip";
1368
1369         public void setEnableCheckInfoTooltip(boolean enableCheckInfoTooltip) {
1370                 boolean old = this.enableCheckInfoTooltip;
1371                 if (old != enableCheckInfoTooltip) {
1372                         this.enableCheckInfoTooltip = enableCheckInfoTooltip;
1373                         propChangeSupport.firePropertyChange(ENABLE_CHECK_INFO_TOOLTIP, old, enableCheckInfoTooltip);
1374                 }
1375         }
1376
1377         /**
1378          * ホイールによるスクロールの単位.<br>
1379          */
1380         private int wheelScrollUnit = 10;
1381
1382         public int getWheelScrollUnit() {
1383                 return wheelScrollUnit;
1384         }
1385
1386         public static final String WHEEL_SCROLL_UNIT = "wheelScrollUnit";
1387
1388         public void setWheelScrollUnit(int wheelScrollUnit) {
1389                 int old = this.wheelScrollUnit;
1390                 if (old != wheelScrollUnit) {
1391                         this.wheelScrollUnit = wheelScrollUnit;
1392                         propChangeSupport.firePropertyChange(WHEEL_SCROLL_UNIT, old, wheelScrollUnit);
1393                 }
1394         }
1395
1396         /**
1397          * 壁紙にオフスクリーン描画を使用するか?.<br>
1398          * (あまり劇的なパフォーマンス効果はない.)
1399          */
1400         private boolean enableOffscreenWallpaper = false;
1401
1402         public boolean isEnableOffscreenWallpaper() {
1403                 return enableOffscreenWallpaper;
1404         }
1405
1406         public static final String ENABLE_OFFSCREEN_WALLPAPER = "enableOffscreenWallpaper";
1407
1408         public void setEnableOffscreenWallpaper(boolean enableOffscreenWallpaper) {
1409                 boolean old = this.enableOffscreenWallpaper;
1410                 if (old != enableOffscreenWallpaper) {
1411                         this.enableOffscreenWallpaper = enableOffscreenWallpaper;
1412                         propChangeSupport.firePropertyChange(ENABLE_OFFSCREEN_WALLPAPER, old, enableOffscreenWallpaper);
1413                 }
1414         }
1415
1416         /**
1417          * 壁紙のオフスクリーンの既定サイズ.
1418          */
1419         private int offscreenWallpaperSize = 300;
1420
1421         public int getOffscreenWallpaperSize() {
1422                 return offscreenWallpaperSize;
1423         }
1424
1425         private static final String OFFSCREEN_WALLPAPER_SIZE = "offscreenWallpaperSize";
1426
1427         public void setOffscreenWallpaperSize(int offscreenWallpaperSize) {
1428                 int old = this.offscreenWallpaperSize;
1429                 if (old != offscreenWallpaperSize) {
1430                         this.offscreenWallpaperSize = offscreenWallpaperSize;
1431                         propChangeSupport.firePropertyChange(OFFSCREEN_WALLPAPER_SIZE, old, offscreenWallpaperSize);
1432                 }
1433         }
1434
1435
1436         /**
1437          * ランダム選択パーツの履歴数
1438          */
1439         private int randomChooserMaxHistory = 10;
1440
1441         public int getRandomChooserMaxHistory() {
1442                 return randomChooserMaxHistory;
1443         }
1444
1445         public static final String RANDOM_CHOOSER_MAX_HISTORY = "randomChooserMaxHistory";
1446
1447         public void setRandomChooserMaxHistory(int randomChooserMaxHistory) {
1448                 int old = this.randomChooserMaxHistory;
1449                 if (old != randomChooserMaxHistory) {
1450                         this.randomChooserMaxHistory = randomChooserMaxHistory;
1451                         propChangeSupport.firePropertyChange(RANDOM_CHOOSER_MAX_HISTORY, old, randomChooserMaxHistory);
1452                 }
1453         }
1454
1455         /**
1456          * デフォルトのフォントサイズ、0以下の場合はシステム既定のまま
1457          */
1458         private int defaultFontSize = 12;
1459
1460         public int getDefaultFontSize() {
1461                 return defaultFontSize;
1462         }
1463
1464         public static final String DEFAULT_FONT_SIZE = "defaultFontSize";
1465
1466         public void setDefaultFontSize(int defaultFontSize) {
1467                 int old = this.defaultFontSize;
1468                 if (old != defaultFontSize) {
1469                         this.defaultFontSize = defaultFontSize;
1470                         propChangeSupport.firePropertyChange(DEFAULT_FONT_SIZE, old, defaultFontSize);
1471                 }
1472         }
1473
1474         /**
1475          * デフォルトのフォントファミリー、カンマ区切り
1476          */
1477         private String fontPriority = "Lucida Grande";
1478
1479         public String getFontPriority() {
1480                 return fontPriority;
1481         }
1482
1483         public static final String FONT_PRIORITY = "fontPriority";
1484
1485         public void setFontPriority(String fontPriority) {
1486                 if (fontPriority == null) {
1487                         throw new IllegalArgumentException();
1488                 }
1489                 String old = this.fontPriority;
1490                 if (old == null ? fontPriority != null : !old.equals(fontPriority)) {
1491                         this.fontPriority = fontPriority;
1492                         propChangeSupport.firePropertyChange(FONT_PRIORITY, old, fontPriority);
1493                 }
1494         }
1495
1496         /**
1497          * ウィンドウの位置、サイズ、スクロールバーの位置、ズームの状態を復元するか?
1498          */
1499         private boolean enableRestoreWindow = false;
1500
1501         public boolean isEnableRestoreWindow() {
1502                 return enableRestoreWindow;
1503         }
1504
1505         public static final String ENABLE_RESTORE_WINDOW = "enableRestoreWindow";
1506
1507         public void setEnableRestoreWindow(boolean enableRestoreWindow) {
1508                 boolean old = this.enableRestoreWindow;
1509                 if (old != enableRestoreWindow) {
1510                         this.enableRestoreWindow = enableRestoreWindow;
1511                         propChangeSupport.firePropertyChange(ENABLE_RESTORE_WINDOW, old, enableRestoreWindow);
1512                 }
1513         }
1514
1515         private boolean enableColorAdvancedSettings = true;
1516
1517         public boolean isEnableColorAdvancedSettings() {
1518                 return enableColorAdvancedSettings;
1519         }
1520
1521         public static final String ENABLE_COLOR_ADVANCED_SETTINGS = "enableColorAdvancedSettings";
1522
1523         public void setEnableColorAdvancedSettings(boolean enableColorAdvancedSettings) {
1524                 boolean old = this.enableColorAdvancedSettings;
1525                 if (old != enableColorAdvancedSettings) {
1526                         this.enableColorAdvancedSettings = enableColorAdvancedSettings;
1527                         propChangeSupport.firePropertyChange(ENABLE_COLOR_ADVANCED_SETTINGS, old, enableRestoreWindow);
1528                 }
1529         }
1530 }