OSDN Git Service

Maven3対応。
[jindolf/Jindolf.git] / src / main / java / jp / sfjp / jindolf / glyph / FontChooser.java
1 /*
2  * font chooser
3  *
4  * License : The MIT License
5  * Copyright(c) 2008 olyutorskii
6  */
7
8 package jp.sfjp.jindolf.glyph;
9
10 import java.awt.BorderLayout;
11 import java.awt.Container;
12 import java.awt.Font;
13 import java.awt.GridBagConstraints;
14 import java.awt.GridBagLayout;
15 import java.awt.Insets;
16 import java.awt.Rectangle;
17 import java.awt.event.ActionEvent;
18 import java.awt.event.ActionListener;
19 import java.awt.event.ItemEvent;
20 import java.awt.event.ItemListener;
21 import java.text.MessageFormat;
22 import javax.swing.BorderFactory;
23 import javax.swing.JButton;
24 import javax.swing.JCheckBox;
25 import javax.swing.JComboBox;
26 import javax.swing.JLabel;
27 import javax.swing.JPanel;
28 import javax.swing.JScrollPane;
29 import javax.swing.JTextField;
30 import javax.swing.border.Border;
31 import javax.swing.event.ListSelectionEvent;
32 import javax.swing.event.ListSelectionListener;
33 import jp.sfjp.jindolf.ResourceManager;
34 import jp.sfjp.jindolf.dxchg.TextPopup;
35 import jp.sfjp.jindolf.log.LogWrapper;
36 import jp.sfjp.jindolf.util.Monodizer;
37
38 /**
39  * 発言表示フォント選択パネル。
40  */
41 @SuppressWarnings("serial")
42 public class FontChooser extends JPanel
43         implements ListSelectionListener,
44                    ActionListener,
45                    ItemListener {
46
47     private static final int[] POINT_SIZES = {
48         8, 10, 12, 16, 18, 24, 32, 36, 48, 72,  // TODO これで十分?
49     };
50     private static final CharSequence PREVIEW_CONTENT;
51     private static final int UNIT_INC = 8;
52
53     private static final LogWrapper LOGGER = new LogWrapper();
54
55     static{
56         PREVIEW_CONTENT =
57                 ResourceManager.getTextFile("resources/font/preview.txt");
58     }
59
60     private FontInfo fontInfo;
61     private FontInfo lastFontInfo;
62
63     private final FontSelectList familySelector;
64     private final JComboBox sizeSelector;
65     private final JCheckBox isBoldCheck;
66     private final JCheckBox isItalicCheck;
67     private final JCheckBox useTextAntiAliaseCheck;
68     private final JCheckBox useFractionalCheck;
69     private final JLabel maxBounds;
70     private final JTextField decodeName;
71     private final FontPreviewer preview;
72     private final JButton resetDefault;
73
74     private boolean maskListener = false;
75
76     /**
77      * コンストラクタ。
78      */
79     public FontChooser(){
80         this(FontInfo.DEFAULT_FONTINFO);
81         return;
82     }
83
84     /**
85      * コンストラクタ。
86      * @param fontInfo 初期フォント設定
87      * @throws NullPointerException 引数がnull
88      */
89     public FontChooser(FontInfo fontInfo)
90             throws NullPointerException{
91         super();
92
93         if(fontInfo == null) throw new NullPointerException();
94         this.fontInfo = fontInfo;
95         this.lastFontInfo = fontInfo;
96
97         logging(this.fontInfo);
98
99         this.familySelector = new FontSelectList();
100
101         this.sizeSelector = new JComboBox();
102         this.sizeSelector.setEditable(true);
103         for(int size : POINT_SIZES){
104             this.sizeSelector.addItem(size);
105         }
106
107         this.isBoldCheck            = new JCheckBox("ボールド");
108         this.isItalicCheck          = new JCheckBox("イタリック");
109         this.useTextAntiAliaseCheck = new JCheckBox("アンチエイリアス");
110         this.useFractionalCheck     = new JCheckBox("サブピクセル精度");
111
112         this.maxBounds = new JLabel();
113
114         this.decodeName = new JTextField();
115         this.decodeName.setEditable(false);
116         this.decodeName.setMargin(new Insets(1, 4, 1, 4));
117         this.decodeName.setComponentPopupMenu(new TextPopup());
118         Monodizer.monodize(this.decodeName);
119
120         this.preview = new FontPreviewer(PREVIEW_CONTENT, this.fontInfo);
121
122         this.resetDefault = new JButton("出荷時に戻す");
123
124         design(this);
125         updateControlls();
126
127         this.familySelector.addListSelectionListener(this);
128         this.sizeSelector  .addActionListener(this);
129
130         this.isBoldCheck           .addItemListener(this);
131         this.isItalicCheck         .addItemListener(this);
132         this.useTextAntiAliaseCheck.addItemListener(this);
133         this.useFractionalCheck    .addItemListener(this);
134
135         this.resetDefault.addActionListener(this);
136
137         return;
138     }
139
140     /**
141      * フォント情報に関するログ出力。
142      * @param info フォント情報
143      */
144     private static void logging(FontInfo info){
145         String form;
146         String logMsg;
147
148         form = "発言表示フォントに{0}が選択されました。";
149         logMsg = MessageFormat.format(form, info.getFont());
150         LOGGER.info(logMsg);
151
152         form = "発言表示のアンチエイリアス指定に{0}が指定されました。";
153         logMsg = MessageFormat.format(form, info.isAntiAliased());
154         LOGGER.info(logMsg);
155
156         form = "発言表示のFractional指定に{0}が指定されました。";
157         logMsg = MessageFormat.format(form, info.usesFractionalMetrics());
158         LOGGER.info(logMsg);
159
160         return;
161     }
162
163     /**
164      * GUIのデザイン、レイアウトを行う。
165      * @param content コンテナ
166      */
167     private void design(Container content){
168         GridBagLayout layout = new GridBagLayout();
169         content.setLayout(layout);
170
171         GridBagConstraints constraints = new GridBagConstraints();
172         constraints.insets = new Insets(5, 5, 5, 5);
173
174         constraints.weightx   = 1.0;
175         constraints.weighty   = 0.0;
176         constraints.gridwidth = GridBagConstraints.REMAINDER;
177         constraints.fill      = GridBagConstraints.BOTH;
178         content.add(createFontPrefPanel(), constraints);
179
180         constraints.weightx   = 1.0;
181         constraints.weighty   = 1.0;
182         constraints.gridwidth = GridBagConstraints.REMAINDER;
183         constraints.fill      = GridBagConstraints.BOTH;
184         content.add(createPreviewPanel(), constraints);
185
186         constraints.weightx   = 1.0;
187         constraints.weighty   = 0.0;
188         constraints.gridwidth = GridBagConstraints.REMAINDER;
189         constraints.fill      = GridBagConstraints.HORIZONTAL;
190         content.add(createFontDecodePanel(), constraints);
191
192         constraints.weightx   = 1.0;
193         constraints.weighty   = 0.0;
194         constraints.gridwidth = 1;
195         constraints.fill      = GridBagConstraints.HORIZONTAL;
196         content.add(this.maxBounds, constraints);
197
198         constraints.weightx   = 0.0;
199         constraints.weighty   = 0.0;
200         constraints.gridwidth = GridBagConstraints.REMAINDER;
201         constraints.fill      = GridBagConstraints.HORIZONTAL;
202         content.add(this.resetDefault, constraints);
203
204         return;
205     }
206
207     /**
208      * フォント設定画面を生成する。
209      * @return フォント設定画面
210      */
211     private JPanel createFontPrefPanel(){
212         JPanel result = new JPanel();
213
214         GridBagLayout layout = new GridBagLayout();
215         GridBagConstraints constraints = new GridBagConstraints();
216         result.setLayout(layout);
217
218         JPanel familyBorderPanel = new JPanel();
219         Border familyBorder =
220                 BorderFactory.createTitledBorder("フォントファミリ選択");
221         familyBorderPanel.setBorder(familyBorder);
222
223         JPanel sizeBorderPanel = new JPanel();
224         Border sizeBorder =
225                 BorderFactory.createTitledBorder("ポイントサイズ指定");
226         sizeBorderPanel.setBorder(sizeBorder);
227
228         Border scrollBorder = BorderFactory.createEmptyBorder(1, 1, 1, 1);
229         this.familySelector.setBorder(scrollBorder);
230         JScrollPane familyScroller = new JScrollPane(this.familySelector);
231
232         familyBorderPanel.setLayout(new BorderLayout());
233         familyBorderPanel.add(familyScroller);
234         constraints.insets     = new Insets(0, 0, 0, 5);
235         constraints.weightx    = 1.0;
236         constraints.weighty    = 0.0;
237         constraints.gridheight = GridBagConstraints.REMAINDER;
238         constraints.fill       = GridBagConstraints.BOTH;
239         result.add(familyBorderPanel, constraints);
240
241         sizeBorderPanel.setLayout(new BorderLayout());
242         sizeBorderPanel.add(this.sizeSelector);
243         constraints.insets     = new Insets(0, 0, 0, 0);
244         constraints.weightx    = 0.0;
245         constraints.gridheight = 1;
246         constraints.fill       = GridBagConstraints.HORIZONTAL;
247         constraints.anchor     = GridBagConstraints.WEST;
248         result.add(sizeBorderPanel, constraints);
249
250         constraints.anchor = GridBagConstraints.NORTHWEST;
251         result.add(this.isBoldCheck,            constraints);
252         result.add(this.isItalicCheck,          constraints);
253         result.add(this.useTextAntiAliaseCheck, constraints);
254         result.add(this.useFractionalCheck,     constraints);
255
256         return result;
257     }
258
259     /**
260      * プレビュー画面を生成する。
261      * @return プレビュー画面
262      */
263     private JPanel createPreviewPanel(){
264         JPanel result = new JPanel();
265         Border border =
266                 BorderFactory.createTitledBorder("フォントプレビュー");
267         result.setBorder(border);
268
269         JScrollPane scroller = new JScrollPane(this.preview);
270         scroller.getVerticalScrollBar().setUnitIncrement(UNIT_INC);
271
272         result.setLayout(new BorderLayout());
273         result.add(scroller);
274
275         return result;
276     }
277
278     /**
279      * フォントデコード名表示パネルを生成する。
280      * @return フォントデコード名表示パネル
281      */
282     private JPanel createFontDecodePanel(){
283         JPanel result = new JPanel();
284
285         GridBagLayout layout = new GridBagLayout();
286         result.setLayout(layout);
287
288         GridBagConstraints constraints = new GridBagConstraints();
289
290         JLabel label = new JLabel("Font.deode() 識別名:");
291
292         constraints.weightx   = 0.0;
293         constraints.weighty   = 0.0;
294         constraints.gridwidth = 1;
295         constraints.fill      = GridBagConstraints.NONE;
296         result.add(label, constraints);
297
298         constraints.weightx   = 1.0;
299         constraints.gridwidth = GridBagConstraints.REMAINDER;
300         constraints.fill      = GridBagConstraints.HORIZONTAL;
301         result.add(this.decodeName, constraints);
302
303         return result;
304     }
305
306     /**
307      * フォント設定を返す。
308      * @return フォント設定
309      */
310     public FontInfo getFontInfo(){
311         return this.fontInfo;
312     }
313
314     /**
315      * フォント設定を適用する。
316      * @param newInfo 新設定
317      * @throws NullPointerException 引数がnull
318      */
319     public void setFontInfo(FontInfo newInfo) throws NullPointerException{
320         if(newInfo == null) throw new NullPointerException();
321
322         FontInfo old = this.fontInfo;
323         if(old.equals(newInfo)) return;
324
325         this.fontInfo = newInfo;
326
327         updateControlls();
328
329         return;
330     }
331
332     /**
333      * フォント設定に合わせてGUIを更新する。
334      * <p>イベント発火は抑止される。
335      */
336     private void updateControlls(){
337         this.maskListener = true;
338
339         Font currentFont = getFontInfo().getFont();
340
341         // フォント名リスト
342         String defaultFamily = currentFont.getFamily();
343         this.familySelector.setSelectedFamily(defaultFamily);
344
345         // サイズ指定コンボボックス
346         Integer selectedInteger = Integer.valueOf(currentFont.getSize());
347         this.sizeSelector.setSelectedItem(selectedInteger);
348         int sizeItems = this.sizeSelector.getItemCount();
349         for(int index = 0; index < sizeItems; index++){
350             Object sizeItem = this.sizeSelector.getItemAt(index);
351             if(sizeItem.equals(selectedInteger)){
352                 this.sizeSelector.setSelectedIndex(index);
353                 break;
354             }
355         }
356
357         // チェックボックス群
358         this.isBoldCheck  .setSelected(currentFont.isBold());
359         this.isItalicCheck.setSelected(currentFont.isItalic());
360         this.useTextAntiAliaseCheck
361             .setSelected(this.fontInfo.isAntiAliased());
362         this.useFractionalCheck
363             .setSelected(this.fontInfo.usesFractionalMetrics());
364
365         // デコード名
366         this.decodeName.setText(FontUtils.getFontDecodeName(currentFont));
367         this.decodeName.setCaretPosition(0);
368
369         // 寸法
370         String form = "最大文字寸法\u0020:\u0020"
371                     + "{0}\u0020pixel幅"
372                     + "\u0020×\u0020"
373                     + "{1}\u0020pixel高";
374         Rectangle rect = this.fontInfo.getMaxCharBounds();
375         String boundInfo =
376                 MessageFormat.format(form, rect.width, rect.height);
377         this.maxBounds.setText(boundInfo);
378
379         // プレビュー
380         this.preview.setFontInfo(this.fontInfo);
381
382         this.maskListener = false;
383
384         return;
385     }
386
387     /**
388      * {@inheritDoc}
389      * @param isVisible trueなら表示 {@inheritDoc}
390      */
391     @Override
392     public void setVisible(boolean isVisible){
393         if(isVisible){
394             updateControlls();
395         }
396         this.lastFontInfo = this.fontInfo;
397
398         super.setVisible(isVisible);
399
400         return;
401     }
402
403     /**
404      * {@inheritDoc}
405      * フォントファミリリスト選択操作のリスナ。
406      * @param event 操作イベント {@inheritDoc}
407      */
408     @Override
409     public void valueChanged(ListSelectionEvent event){
410         if(this.maskListener) return;
411
412         if(event.getSource() != this.familySelector) return;
413         if(event.getValueIsAdjusting()) return;
414
415         String familyName = this.familySelector.getSelectedFamily();
416         if(familyName == null) return;
417
418         Font currentFont = getFontInfo().getFont();
419         int style = currentFont.getStyle();
420         int size  = currentFont.getSize();
421
422         Font newFont = new Font(familyName, style, size);
423         FontInfo newInfo = this.fontInfo.deriveFont(newFont);
424
425         setFontInfo(newInfo);
426
427         return;
428     }
429
430     /**
431      * {@inheritDoc}
432      * ボタン操作及びフォントサイズ指定コンボボックス操作のリスナ。
433      * @param event 操作イベント {@inheritDoc}
434      */
435     @Override
436     public void actionPerformed(ActionEvent event){
437         if(this.maskListener) return;
438
439         Object source = event.getSource();
440
441         if(source == this.sizeSelector){
442             actionFontSizeSelected();
443         }else if(source == this.resetDefault){
444             setFontInfo(FontInfo.DEFAULT_FONTINFO);
445         }
446
447         return;
448     }
449
450     /**
451      * フォントサイズ変更処理。
452      */
453     private void actionFontSizeSelected(){
454         Object selected = this.sizeSelector.getSelectedItem();
455         if(selected == null) return;
456
457         Integer selectedInteger;
458         if(selected instanceof Integer){
459             selectedInteger = (Integer) selected;
460         }else{
461             try{
462                 selectedInteger = Integer.valueOf(selected.toString());
463             }catch(NumberFormatException e){
464                 selectedInteger =  this.lastFontInfo.getFont().getSize();
465             }
466         }
467
468         if(selectedInteger <= 0){
469             selectedInteger = this.lastFontInfo.getFont().getSize();
470         }
471
472         float fontSize = selectedInteger.floatValue();
473         Font newFont = getFontInfo().getFont().deriveFont(fontSize);
474         FontInfo newInfo = getFontInfo().deriveFont(newFont);
475
476         setFontInfo(newInfo);
477
478         return;
479     }
480
481     /**
482      * {@inheritDoc}
483      * チェックボックス操作のリスナ。
484      * @param event 操作イベント {@inheritDoc}
485      */
486     @Override
487     public void itemStateChanged(ItemEvent event){
488         if(this.maskListener) return;
489
490         Object source = event.getSource();
491
492         if(   source != this.isBoldCheck
493            && source != this.isItalicCheck
494            && source != this.useTextAntiAliaseCheck
495            && source != this.useFractionalCheck     ){
496             return;
497         }
498
499         FontInfo newInfo = getFontInfo();
500
501         int style = 0 | Font.PLAIN;
502         if(this.isBoldCheck.isSelected()){
503             style = style | Font.BOLD;
504         }
505         if(this.isItalicCheck.isSelected()){
506             style = style | Font.ITALIC;
507         }
508         Font newFont = newInfo.getFont();
509         if(newFont.getStyle() != style){
510             newFont = newFont.deriveFont(style);
511             newInfo = newInfo.deriveFont(newFont);
512         }
513
514         boolean isAntiAliases = this.useTextAntiAliaseCheck.isSelected();
515         boolean useFractional = this.useFractionalCheck    .isSelected();
516         newInfo = newInfo.deriveRenderContext(isAntiAliases, useFractional);
517
518         setFontInfo(newInfo);
519
520         return;
521     }
522
523 }