OSDN Git Service

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