OSDN Git Service

181a56cc52bf4ba48cd3711e6980738b53f7a90b
[jindolf/Jindolf.git] / src / main / java / jp / sfjp / jindolf / config / AppSetting.java
1 /*
2  * application settings
3  *
4  * License : The MIT License
5  * Copyright(c) 2008 olyutorskii
6  */
7
8 package jp.sfjp.jindolf.config;
9
10 import java.awt.Font;
11 import java.awt.Rectangle;
12 import java.awt.image.BufferedImage;
13 import java.io.File;
14 import java.io.IOException;
15 import java.nio.file.Path;
16 import java.nio.file.Paths;
17 import java.util.HashMap;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.logging.Logger;
21 import javax.imageio.ImageIO;
22 import jp.sfjp.jindolf.data.Avatar;
23 import jp.sfjp.jindolf.data.DialogPref;
24 import jp.sfjp.jindolf.glyph.Font2Json;
25 import jp.sfjp.jindolf.glyph.FontInfo;
26 import jp.sfjp.jindolf.net.ProxyInfo;
27 import jp.sfjp.jindolf.view.AvatarPics;
28 import jp.sourceforge.jovsonz.JsBoolean;
29 import jp.sourceforge.jovsonz.JsObject;
30 import jp.sourceforge.jovsonz.JsPair;
31 import jp.sourceforge.jovsonz.JsString;
32 import jp.sourceforge.jovsonz.JsTypes;
33 import jp.sourceforge.jovsonz.JsValue;
34
35 /**
36  * アプリケーションの各種設定。
37  */
38 public class AppSetting{
39
40     // デフォルトのウィンドウサイズ
41     private static final int DEF_WIDTH  = 800;
42     private static final int DEF_HEIGHT = 600;
43
44     private static final String HASH_FONT        = "font";
45     private static final String HASH_USEBODYICON = "useBodyIcon";
46     private static final String HASH_USEMONOTOMB = "useMonoTomb";
47     private static final String HASH_SIMPLEMODE  = "isSimpleMode";
48     private static final String HASH_ALIGNBALOON = "alignBaloonWidth";
49     private static final String HASH_PROXY       = "proxy";
50
51     private static final Logger LOGGER = Logger.getAnonymousLogger();
52
53
54     private final OptionInfo optInfo;
55     private final ConfigStore configStore;
56     private final Rectangle frameRect;
57
58     private FontInfo fontInfo;
59
60     private ProxyInfo proxyInfo = ProxyInfo.DEFAULT;
61
62     private DialogPref dialogPref = new DialogPref();
63
64     private JsValue loadedNetConfig;
65     private JsValue loadedTalkConfig;
66
67     private final Map<String, BufferedImage> avatarFaceMap = new HashMap<>();
68     private final Map<String, BufferedImage> avatarBodyMap = new HashMap<>();
69
70
71     /**
72      * コンストラクタ。
73      * @param info コマンドライン引数
74      */
75     public AppSetting(OptionInfo info){
76         super();
77
78         this.optInfo = info;
79         this.configStore = parseConfigStore(this.optInfo);
80         this.frameRect = parseGeometrySetting(this.optInfo);
81
82         return;
83     }
84
85
86     /**
87      * 設定格納ディレクトリ関係の解析。
88      * @param option コマンドラインオプション情報
89      * @return 設定ディレクトリ情報
90      */
91     private static ConfigStore parseConfigStore(OptionInfo option){
92         CmdOption opt = option.getExclusiveOption(CmdOption.OPT_CONFDIR,
93                                                   CmdOption.OPT_NOCONF );
94
95         boolean useConfig;
96         boolean isImplicitPath;
97         File configPath;
98
99         if(opt == CmdOption.OPT_NOCONF){
100             useConfig = false;
101             isImplicitPath = true;
102             configPath = null;
103         }else if(opt == CmdOption.OPT_CONFDIR){
104             useConfig = true;
105             isImplicitPath = false;
106             String optPath = option.getStringArg(opt);
107             configPath = FileUtils.supplyFullPath(new File(optPath));
108         }else{
109             useConfig = true;
110             isImplicitPath = true;
111             configPath = ConfigFile.getImplicitConfigDirectory();
112         }
113
114         ConfigStore result =
115                 new ConfigStore(useConfig, isImplicitPath, configPath);
116
117         return result;
118     }
119
120     /**
121      * ウィンドウジオメトリ関係の設定。
122      * @param option コマンドラインオプション情報
123      * @return ウィンドウ矩形。
124      */
125     private static Rectangle parseGeometrySetting(OptionInfo option){
126         Rectangle result = new Rectangle(Integer.MIN_VALUE,
127                                          Integer.MIN_VALUE,
128                                          DEF_WIDTH,
129                                          DEF_HEIGHT );
130
131         Integer ival;
132
133         ival = option.initialFrameWidth();
134         if(ival != null) result.width = ival;
135
136         ival = option.initialFrameHeight();
137         if(ival != null) result.height = ival;
138
139         ival = option.initialFrameXpos();
140         if(ival != null) result.x = ival;
141
142         ival = option.initialFrameYpos();
143         if(ival != null) result.y = ival;
144
145         return result;
146     }
147
148
149     /**
150      * フォントオプションの解析。
151      * @param baseFont 元のフォント設定。
152      * @return コマンドライン設定で補正されたフォント設定
153      */
154     private FontInfo parseFontOption(FontInfo baseFont){
155         FontInfo result;
156
157         String fontName = this.optInfo.getStringArg(CmdOption.OPT_INITFONT);
158         if(fontName != null){
159             Font font = Font.decode(fontName);
160             result = baseFont.deriveFont(font);
161         }else{
162             result = baseFont;
163         }
164
165         Boolean useAntiAlias =
166                 this.optInfo.getBooleanArg(CmdOption.OPT_ANTIALIAS);
167         if(useAntiAlias == null){
168             useAntiAlias = baseFont.isAntiAliased();
169         }
170
171         Boolean useFractional =
172                 this.optInfo.getBooleanArg(CmdOption.OPT_FRACTIONAL);
173         if(useFractional == null){
174             useFractional = baseFont.usesFractionalMetrics();
175         }
176
177         result = result.deriveRenderContext(useAntiAlias,
178                                             useFractional );
179
180         return result;
181     }
182
183     /**
184      * コマンドラインオプション情報を返す。
185      * @return コマンドラインオプション情報
186      */
187     public OptionInfo getOptionInfo(){
188         return this.optInfo;
189     }
190
191     /**
192      * 設定格納情報を返す。
193      * @return 設定格納情報
194      */
195     public ConfigStore getConfigStore(){
196         return this.configStore;
197     }
198
199     /**
200      * 初期のフレーム幅を返す。
201      * @return 初期のフレーム幅
202      */
203     public int initialFrameWidth(){
204         int width = this.frameRect.width;
205         return width;
206     }
207
208     /**
209      * 初期のフレーム高を返す。
210      * @return 初期のフレーム高
211      */
212     public int initialFrameHeight(){
213         int height = this.frameRect.height;
214         return height;
215     }
216
217     /**
218      * 初期のフレーム位置のX座標を返す。
219      * 特に指示されていなければInteger.MIN_VALUEを返す。
220      * @return 初期のフレーム位置のX座標
221      */
222     public int initialFrameXpos(){
223         int xPos = this.frameRect.x;
224         return xPos;
225     }
226
227     /**
228      * 初期のフレーム位置のY座標を返す。
229      * 特に指示されていなければInteger.MIN_VALUEを返す。
230      * @return 初期のフレーム位置のY座標
231      */
232     public int initialFrameYpos(){
233         int yPos = this.frameRect.y;
234         return yPos;
235     }
236
237     /**
238      * フォント設定を返す。
239      * @return フォント設定
240      */
241     public FontInfo getFontInfo(){
242         if(this.fontInfo == null){
243             this.fontInfo = parseFontOption(FontInfo.DEFAULT_FONTINFO);
244         }
245         return this.fontInfo;
246     }
247
248     /**
249      * フォント設定を更新する。
250      * @param fontInfo フォント設定
251      */
252     public void setFontInfo(FontInfo fontInfo){
253         this.fontInfo = fontInfo;
254         return;
255     }
256
257     /**
258      * プロクシ設定を返す。
259      * @return プロクシ設定
260      */
261     public ProxyInfo getProxyInfo(){
262         return this.proxyInfo;
263     }
264
265     /**
266      * プロクシ設定を更新する。
267      * @param proxyInfo プロクシ設定。nullならプロクシなしと解釈される。
268      */
269     public void setProxyInfo(ProxyInfo proxyInfo){
270         if(proxyInfo == null) this.proxyInfo = ProxyInfo.DEFAULT;
271         else                  this.proxyInfo = proxyInfo;
272         return;
273     }
274
275     /**
276      * 発言表示設定を返す。
277      * @return 表示設定
278      */
279     public DialogPref getDialogPref(){
280         return this.dialogPref;
281     }
282
283     /**
284      * 発言表示設定を返す。
285      * @param pref 表示設定
286      */
287     public void setDialogPref(DialogPref pref){
288         if(pref == null) this.dialogPref = new DialogPref();
289         else             this.dialogPref = pref;
290         return;
291     }
292
293     /**
294      * ネットワーク設定をロードする。
295      */
296     private void loadNetConfig(){
297         JsObject root = this.configStore.loadNetConfig();
298         if(root == null) return;
299         this.loadedNetConfig = root;
300
301         JsValue value = root.getValue(HASH_PROXY);
302         if( ! (value instanceof JsObject) ) return;
303         JsObject proxy = (JsObject) value;
304
305         ProxyInfo info = ProxyInfo.decodeJson(proxy);
306
307         setProxyInfo(info);
308
309         return;
310     }
311
312     /**
313      * 会話表示設定をロードする。
314      */
315     private void loadTalkConfig(){
316         JsObject root = this.configStore.loadTalkConfig();
317         if(root == null) return;
318         this.loadedTalkConfig = root;
319
320         JsValue value = root.getValue(HASH_FONT);
321         if(value instanceof JsObject){
322             JsObject font = (JsObject) value;
323             FontInfo info = Font2Json.decodeJson(font);
324             info = parseFontOption(info);
325             setFontInfo(info);
326         }
327
328         DialogPref pref = new DialogPref();
329         JsBoolean boolValue;
330         value = root.getValue(HASH_USEBODYICON);
331         if(value instanceof JsBoolean){
332             boolValue = (JsBoolean) value;
333             pref.setBodyImageSetting(boolValue.booleanValue());
334         }
335         value = root.getValue(HASH_USEMONOTOMB);
336         if(value instanceof JsBoolean){
337             boolValue = (JsBoolean) value;
338             pref.setMonoImageSetting(boolValue.booleanValue());
339         }
340         value = root.getValue(HASH_SIMPLEMODE);
341         if(value instanceof JsBoolean){
342             boolValue = (JsBoolean) value;
343             pref.setSimpleMode(boolValue.booleanValue());
344         }
345         value = root.getValue(HASH_ALIGNBALOON);
346         if(value instanceof JsBoolean){
347             boolValue = (JsBoolean) value;
348             pref.setAlignBalooonWidthSetting(boolValue.booleanValue());
349         }
350         setDialogPref(pref);
351
352         return;
353     }
354
355     /**
356      * JSONをパースしAvatar-Image間マップに反映させる。
357      *
358      * @param json JSON構造
359      * @param map マップ
360      */
361     private void parseImgMap(JsObject json, Map<String, BufferedImage> map){
362         Path imgDir = this.configStore.getLocalImgDir();
363
364         List<JsPair> pairList = json.getPairList();
365         for(JsPair pair : pairList){
366             String avatarId = pair.getName();
367             JsValue value = pair.getValue();
368
369             if(value.getJsTypes() != JsTypes.STRING) continue;
370             JsString sVal = (JsString)value;
371             String imgName = sVal.toRawString();
372
373             Path imgPath = Paths.get(imgName);
374             Path full = imgDir.resolve(imgPath);
375             File file = full.toFile();
376             if(        ! file.isAbsolute()
377                     || ! file.exists()
378                     || ! file.isFile()
379                     || ! file.canRead() ){
380                 LOGGER.info("failed to access local image " + file.getPath());
381                 continue;
382             }
383
384             BufferedImage image;
385             try {
386                 image = ImageIO.read(file);
387             }catch(IOException e){
388                 LOGGER.info("failed to load local image " + file.getPath());
389                 continue;
390             }
391
392             map.put(avatarId, image);
393         }
394
395         return;
396     }
397
398     /**
399      * ローカル画像設定をロードする。
400      */
401     private void loadLocalImageConfig(){
402         JsObject root = this.configStore.loadLocalImgConfig();
403         if(root == null) return;
404
405         JsValue faceConfig = root.getValue("avatarFace");
406         JsValue bodyConfig = root.getValue("avatarBody");
407         if(faceConfig.getJsTypes() != JsTypes.OBJECT) return;
408         if(bodyConfig.getJsTypes() != JsTypes.OBJECT) return;
409
410         JsObject jsonFace = (JsObject) faceConfig;
411         parseImgMap(jsonFace, this.avatarFaceMap);
412
413
414         JsObject jsonBody = (JsObject) bodyConfig;
415         parseImgMap(jsonBody, this.avatarBodyMap);
416
417         return;
418     }
419
420     /**
421      * ローカル代替イメージを画像キャッシュに反映させる。
422      *
423      * @param avatarPics 画像キャッシュ
424      */
425     public void applyLocalImage(AvatarPics avatarPics){
426         BufferedImage graveImage     = this.avatarFaceMap.get("tomb");
427         BufferedImage graveBodyImage = this.avatarBodyMap.get("tomb");
428
429         avatarPics.setGraveImage(graveImage);
430         avatarPics.setGraveBodyImage(graveBodyImage);
431
432         for(Avatar avatar : Avatar.getPredefinedAvatarList()){
433             String avatarId = avatar.getIdentifier();
434
435             BufferedImage faceImage = this.avatarFaceMap.get(avatarId);
436             BufferedImage bodyImage = this.avatarBodyMap.get(avatarId);
437
438             avatarPics.setAvatarFaceImage(avatar, faceImage);
439             avatarPics.setAvatarBodyImage(avatar, bodyImage);
440         }
441
442         return;
443     }
444
445     /**
446      * ネットワーク設定をセーブする。
447      */
448     private void saveNetConfig(){
449         if( ! getConfigStore().useStoreFile() ) return;
450
451         JsObject root = new JsObject();
452         JsObject proxy = ProxyInfo.buildJson(getProxyInfo());
453         root.putValue(HASH_PROXY, proxy);
454
455         if(this.loadedNetConfig != null){
456             if(this.loadedNetConfig.equals(root)) return;
457         }
458
459         this.configStore.saveNetConfig(root);
460
461         return;
462     }
463
464     /**
465      * 会話表示設定をセーブする。
466      */
467     private void saveTalkConfig(){
468         if( ! getConfigStore().useStoreFile() ) return;
469
470         JsObject root = new JsObject();
471
472         JsObject font = Font2Json.buildJson(getFontInfo());
473         root.putValue(HASH_FONT, font);
474
475         DialogPref pref = getDialogPref();
476         JsPair useBodyIcon =
477                 new JsPair(HASH_USEBODYICON, pref.useBodyImage());
478         JsPair useMonoTomb =
479                 new JsPair(HASH_USEMONOTOMB, pref.useMonoImage());
480         JsPair isSimple =
481                 new JsPair(HASH_SIMPLEMODE, pref.isSimpleMode());
482         JsPair alignBaloon =
483                 new JsPair(HASH_ALIGNBALOON, pref.alignBaloonWidth());
484         root.putPair(useBodyIcon);
485         root.putPair(useMonoTomb);
486         root.putPair(isSimple);
487         root.putPair(alignBaloon);
488
489         if(this.loadedTalkConfig != null){
490             if(this.loadedTalkConfig.equals(root)) return;
491         }
492
493         this.configStore.saveTalkConfig(root);
494
495         return;
496     }
497
498     /**
499      * 各種設定を設定格納ディレクトリからロードする。
500      */
501     public void loadConfig(){
502         loadNetConfig();
503         loadTalkConfig();
504         loadLocalImageConfig();
505         return;
506     }
507
508     /**
509      * 各種設定を設定格納ディレクトリへセーブする。
510      */
511     public void saveConfig(){
512         saveNetConfig();
513         saveTalkConfig();
514         return;
515     }
516
517 }