OSDN Git Service

Merge commit '2458eff3aea04f67893bc824b5cf896fbb767332'
[jindolf/Jindolf.git] / src / main / java / jp / sourceforge / jindolf / FontChooser.java
diff --git a/src/main/java/jp/sourceforge/jindolf/FontChooser.java b/src/main/java/jp/sourceforge/jindolf/FontChooser.java
new file mode 100644 (file)
index 0000000..02a88fd
--- /dev/null
@@ -0,0 +1,637 @@
+/*\r
+ * font chooser\r
+ *\r
+ * Copyright(c) 2008 olyutorskii\r
+ * $Id: FontChooser.java 956 2009-12-13 15:14:07Z olyutorskii $\r
+ */\r
+\r
+package jp.sourceforge.jindolf;\r
+\r
+import java.awt.BorderLayout;\r
+import java.awt.Color;\r
+import java.awt.Container;\r
+import java.awt.Dimension;\r
+import java.awt.Font;\r
+import java.awt.Graphics;\r
+import java.awt.Graphics2D;\r
+import java.awt.GridBagConstraints;\r
+import java.awt.GridBagLayout;\r
+import java.awt.Insets;\r
+import java.awt.Rectangle;\r
+import java.awt.event.ActionEvent;\r
+import java.awt.event.ActionListener;\r
+import java.awt.event.ItemEvent;\r
+import java.awt.event.ItemListener;\r
+import java.awt.font.FontRenderContext;\r
+import java.awt.geom.AffineTransform;\r
+import java.awt.geom.Rectangle2D;\r
+import java.io.IOException;\r
+import javax.swing.BorderFactory;\r
+import javax.swing.JButton;\r
+import javax.swing.JCheckBox;\r
+import javax.swing.JComboBox;\r
+import javax.swing.JComponent;\r
+import javax.swing.JLabel;\r
+import javax.swing.JList;\r
+import javax.swing.JPanel;\r
+import javax.swing.JScrollPane;\r
+import javax.swing.JTextField;\r
+import javax.swing.ListSelectionModel;\r
+import javax.swing.border.Border;\r
+import javax.swing.event.ListSelectionEvent;\r
+import javax.swing.event.ListSelectionListener;\r
+\r
+/**\r
+ * 発言表示フォント選択パネル。\r
+ */\r
+@SuppressWarnings("serial")\r
+public class FontChooser extends JPanel\r
+        implements ListSelectionListener,\r
+                   ActionListener,\r
+                   ItemListener{\r
+\r
+    private static final Integer[] POINT_SIZES = {\r
+        8, 10, 12, 16, 18, 24, 32, 36, 48, 72,  // TODO これで十分?\r
+    };\r
+    private static final CharSequence PREVIEW_CONTENT;\r
+\r
+    static{\r
+        CharSequence resourceText;\r
+        try{\r
+            resourceText = Jindolf.loadResourceText("resources/preview.txt");\r
+        }catch(IOException e){\r
+            resourceText = "ABC";\r
+        }\r
+        PREVIEW_CONTENT = resourceText;\r
+    }\r
+\r
+    private FontInfo fontInfo;\r
+    private FontInfo lastFontInfo;\r
+\r
+    private final JList familySelector;\r
+    private final JComboBox sizeSelector;\r
+    private final JCheckBox isBoldCheck;\r
+    private final JCheckBox isItalicCheck;\r
+    private final JCheckBox useTextAntiAliaseCheck;\r
+    private final JCheckBox useFractionalCheck;\r
+    private final JLabel maxBounds;\r
+    private final JTextField decodeName;\r
+    private final FontPreview preview;\r
+    private final JButton resetDefault;\r
+\r
+    private boolean maskListener = false;\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     */\r
+    public FontChooser(){\r
+        this(FontInfo.DEFAULT_FONTINFO);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     * @param fontInfo 初期フォント設定\r
+     * @throws NullPointerException 引数がnull\r
+     */\r
+    public FontChooser(FontInfo fontInfo)\r
+            throws NullPointerException{\r
+        super();\r
+\r
+        if(fontInfo == null) throw new NullPointerException();\r
+        this.fontInfo = fontInfo;\r
+        this.lastFontInfo = fontInfo;\r
+\r
+        Jindolf.logger().info(\r
+                  "デフォルトの発言表示フォントに"\r
+                + this.fontInfo.getFont()\r
+                + "が選択されました" );\r
+        Jindolf.logger().info(\r
+                  "発言表示のアンチエイリアス指定に"\r
+                + this.fontInfo.getFontRenderContext().isAntiAliased()\r
+                + "が指定されました" );\r
+        Jindolf.logger().info(\r
+                  "発言表示のFractional指定に"\r
+                + this.fontInfo.getFontRenderContext().usesFractionalMetrics()\r
+                + "が指定されました" );\r
+\r
+        this.familySelector = new JList(FontUtils.createFontSet().toArray());\r
+        this.familySelector.setVisibleRowCount(-1);\r
+        this.familySelector\r
+            .setSelectionMode(ListSelectionModel.SINGLE_SELECTION);\r
+\r
+        this.sizeSelector = new JComboBox();\r
+        this.sizeSelector.setEditable(true);\r
+        this.sizeSelector.setActionCommand(ActionManager.CMD_FONTSIZESEL);\r
+        for(Integer size : POINT_SIZES){\r
+            this.sizeSelector.addItem(size);\r
+        }\r
+\r
+        this.isBoldCheck            = new JCheckBox("ボールド");\r
+        this.isItalicCheck          = new JCheckBox("イタリック");\r
+        this.useTextAntiAliaseCheck = new JCheckBox("アンチエイリアス");\r
+        this.useFractionalCheck     = new JCheckBox("サブピクセル精度");\r
+\r
+        this.maxBounds = new JLabel();\r
+\r
+        this.decodeName = new JTextField();\r
+        this.decodeName.setEditable(false);\r
+        this.decodeName.setMargin(new Insets(1, 4, 1, 4));\r
+        this.decodeName.setComponentPopupMenu(new TextPopup());\r
+        Monodizer.monodize(this.decodeName);\r
+\r
+        this.preview = new FontPreview(PREVIEW_CONTENT, this.fontInfo);\r
+\r
+        this.resetDefault = new JButton("出荷時に戻す");\r
+        this.resetDefault.addActionListener(this);\r
+\r
+        design(this);\r
+        updateControlls();\r
+        updatePreview();\r
+\r
+        this.familySelector.addListSelectionListener(this);\r
+        this.sizeSelector  .addActionListener(this);\r
+\r
+        this.isBoldCheck           .addItemListener(this);\r
+        this.isItalicCheck         .addItemListener(this);\r
+        this.useTextAntiAliaseCheck.addItemListener(this);\r
+        this.useFractionalCheck    .addItemListener(this);\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * GUIのデザイン、レイアウトを行う。\r
+     * @param content コンテナ\r
+     */\r
+    private void design(Container content){\r
+        GridBagLayout layout = new GridBagLayout();\r
+        GridBagConstraints constraints = new GridBagConstraints();\r
+\r
+        content.setLayout(layout);\r
+\r
+        Border border;\r
+        JPanel panel;\r
+\r
+        JComponent fontPref = createFontPrefPanel();\r
+\r
+        constraints.insets = new Insets(5, 5, 5, 5);\r
+\r
+        constraints.weightx = 1.0;\r
+        constraints.weighty = 0.0;\r
+        constraints.gridwidth = GridBagConstraints.REMAINDER;\r
+        constraints.fill = GridBagConstraints.BOTH;\r
+        content.add(fontPref, constraints);\r
+\r
+        constraints.weightx = 1.0;\r
+        constraints.weighty = 1.0;\r
+        constraints.gridwidth = GridBagConstraints.REMAINDER;\r
+        constraints.fill = GridBagConstraints.BOTH;\r
+        border = BorderFactory.createTitledBorder("プレビュー");\r
+        panel = new JPanel();\r
+        panel.add(this.preview);\r
+        panel.setBorder(border);\r
+        content.add(createPreviewPanel(), constraints);\r
+\r
+        constraints.weightx = 1.0;\r
+        constraints.weighty = 0.0;\r
+        constraints.gridwidth = GridBagConstraints.REMAINDER;\r
+        constraints.fill = GridBagConstraints.HORIZONTAL;\r
+        content.add(createFontDecodePanel(), constraints);\r
+\r
+        constraints.insets = new Insets(5, 5, 5, 5);\r
+        constraints.weightx = 1.0;\r
+        constraints.weighty = 0.0;\r
+        constraints.gridwidth = 1;\r
+        constraints.fill = GridBagConstraints.HORIZONTAL;\r
+        content.add(this.maxBounds, constraints);\r
+\r
+        constraints.weightx = 0.0;\r
+        constraints.weighty = 0.0;\r
+        constraints.gridwidth = GridBagConstraints.REMAINDER;\r
+        constraints.fill = GridBagConstraints.HORIZONTAL;\r
+        content.add(this.resetDefault, constraints);\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * フォント設定画面を生成する。\r
+     * @return フォント設定画面\r
+     */\r
+    private JComponent createFontPrefPanel(){\r
+        JPanel result = new JPanel();\r
+\r
+        GridBagLayout layout = new GridBagLayout();\r
+        GridBagConstraints constraints = new GridBagConstraints();\r
+        result.setLayout(layout);\r
+\r
+        Border border;\r
+\r
+        constraints.insets = new Insets(0, 0, 0, 5);\r
+        constraints.weightx = 1.0;\r
+        constraints.weighty = 0.0;\r
+        constraints.gridheight = GridBagConstraints.REMAINDER;\r
+        constraints.fill = GridBagConstraints.BOTH;\r
+        border = BorderFactory.createEmptyBorder(1, 1, 1, 1);\r
+        this.familySelector.setBorder(border);\r
+        JScrollPane familyScroller = new JScrollPane(this.familySelector);\r
+        border = BorderFactory.createTitledBorder("フォントファミリ選択");\r
+        JPanel familyPanel = new JPanel();\r
+        familyPanel.setLayout(new BorderLayout());\r
+        familyPanel.add(familyScroller, BorderLayout.CENTER);\r
+        familyPanel.setBorder(border);\r
+        result.add(familyPanel, constraints);\r
+\r
+        constraints.insets = new Insets(0, 0, 0, 0);\r
+        constraints.weightx = 0.0;\r
+        constraints.gridheight = 1;\r
+        constraints.fill = GridBagConstraints.HORIZONTAL;\r
+        constraints.anchor = GridBagConstraints.WEST;\r
+\r
+        border = BorderFactory.createTitledBorder("ポイントサイズ指定");\r
+        JPanel panel = new JPanel();\r
+        panel.add(this.sizeSelector);\r
+        panel.setBorder(border);\r
+        result.add(panel, constraints);\r
+\r
+        constraints.anchor = GridBagConstraints.NORTHWEST;\r
+        result.add(this.isBoldCheck, constraints);\r
+        result.add(this.isItalicCheck, constraints);\r
+        result.add(this.useTextAntiAliaseCheck, constraints);\r
+        result.add(this.useFractionalCheck, constraints);\r
+\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * プレビュー画面を生成する。\r
+     * @return プレビュー画面\r
+     */\r
+    private JComponent createPreviewPanel(){\r
+        JPanel result = new JPanel();\r
+\r
+        JScrollPane scroller = new JScrollPane(this.preview);\r
+        scroller.getVerticalScrollBar().setUnitIncrement(8);\r
+\r
+        Border border;\r
+        border = BorderFactory.createTitledBorder("プレビュー");\r
+        result.setBorder(border);\r
+        result.setLayout(new BorderLayout());\r
+        result.add(scroller, BorderLayout.CENTER);\r
+\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * フォントデコード名表示パネルを生成する。\r
+     * @return フォントデコード名表示パネル\r
+     */\r
+    private JComponent createFontDecodePanel(){\r
+        JPanel result = new JPanel();\r
+\r
+        GridBagLayout layout = new GridBagLayout();\r
+        GridBagConstraints constraints = new GridBagConstraints();\r
+        result.setLayout(layout);\r
+\r
+        constraints.weightx = 0.0;\r
+        constraints.weighty = 0.0;\r
+        constraints.gridwidth = 1;\r
+        constraints.fill = GridBagConstraints.NONE;\r
+        result.add(new JLabel("Font.deode() 識別名:"), constraints);\r
+\r
+        constraints.weightx = 1.0;\r
+        constraints.gridwidth = GridBagConstraints.REMAINDER;\r
+        constraints.fill = GridBagConstraints.HORIZONTAL;\r
+        result.add(this.decodeName, constraints);\r
+\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * フォント設定を返す。\r
+     * @return フォント設定\r
+     */\r
+    public FontInfo getFontInfo(){\r
+        return this.fontInfo;\r
+    }\r
+\r
+    /**\r
+     * フォント設定を適用する。\r
+     * @param newInfo 新設定\r
+     * @throws NullPointerException 引数がnull\r
+     */\r
+    public void setFontInfo(FontInfo newInfo) throws NullPointerException{\r
+        if(newInfo == null) throw new NullPointerException();\r
+\r
+        FontInfo old = this.fontInfo;\r
+        if(old.equals(newInfo)) return;\r
+\r
+        this.fontInfo = newInfo;\r
+\r
+        updateControlls();\r
+        updatePreview();\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 選択されたフォントを返す。\r
+     * @return フォント\r
+     */\r
+    private Font getSelectedFont(){\r
+        return this.fontInfo.getFont();\r
+    }\r
+\r
+    /**\r
+     * 設定されたフォント描画設定を返す。\r
+     * @return 描画設定\r
+     */\r
+    protected FontRenderContext getFontRenderContext(){\r
+        return this.fontInfo.getFontRenderContext();\r
+    }\r
+\r
+    /**\r
+     * フォント設定に合わせてプレビュー画面を更新する。\r
+     */\r
+    private void updatePreview(){\r
+        this.preview.setFontInfo(this.fontInfo);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * フォント設定に合わせてGUIを更新する。\r
+     */\r
+    private void updateControlls(){\r
+        this.maskListener = true;\r
+\r
+        Font currentFont = getSelectedFont();\r
+        FontRenderContext currentContext = getFontRenderContext();\r
+\r
+        String defaultFamily = currentFont.getFamily();\r
+        this.familySelector.setSelectedValue(defaultFamily, true);\r
+\r
+        Integer selectedInteger = Integer.valueOf(currentFont.getSize());\r
+        this.sizeSelector.setSelectedItem(selectedInteger);\r
+        int sizeItems = this.sizeSelector.getItemCount();\r
+        for(int index = 0; index <= sizeItems - 1; index++){\r
+            Object sizeItem = this.sizeSelector.getItemAt(index);\r
+            if(sizeItem.equals(selectedInteger)){\r
+                this.sizeSelector.setSelectedIndex(index);\r
+                break;\r
+            }\r
+        }\r
+\r
+        this.isBoldCheck  .setSelected(currentFont.isBold());\r
+        this.isItalicCheck.setSelected(currentFont.isItalic());\r
+\r
+        this.useTextAntiAliaseCheck\r
+            .setSelected(currentContext.isAntiAliased());\r
+        this.useFractionalCheck\r
+            .setSelected(currentContext.usesFractionalMetrics());\r
+\r
+        this.decodeName.setText(FontUtils.getFontDecodeName(currentFont));\r
+        this.decodeName.setCaretPosition(0);\r
+\r
+        Rectangle2D r2d = currentFont.getMaxCharBounds(currentContext);\r
+        Rectangle rect = r2d.getBounds();\r
+        String boundInfo =  "最大文字寸法 : "\r
+                          + rect.width\r
+                          + " pixel幅 × "\r
+                          + rect.height\r
+                          + " pixel高";\r
+        this.maxBounds.setText(boundInfo);\r
+\r
+        this.maskListener = false;\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * ダイアログの表示・非表示。\r
+     * ダイアログが閉じられるまで制御を返さない。\r
+     * @param isVisible trueなら表示 {@inheritDoc}\r
+     */\r
+    @Override\r
+    public void setVisible(boolean isVisible){\r
+        if(isVisible){\r
+            updateControlls();\r
+            updatePreview();\r
+        }\r
+        this.lastFontInfo = this.fontInfo;\r
+\r
+        super.setVisible(isVisible);\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * チェックボックス操作のリスナ。\r
+     * @param event 操作イベント {@inheritDoc}\r
+     */\r
+    public void itemStateChanged(ItemEvent event){\r
+        if(this.maskListener) return;\r
+\r
+        Object source = event.getSource();\r
+\r
+        if(   source != this.isBoldCheck\r
+           && source != this.isItalicCheck\r
+           && source != this.useTextAntiAliaseCheck\r
+           && source != this.useFractionalCheck     ){\r
+            return;\r
+        }\r
+\r
+        int style = 0 | Font.PLAIN;\r
+        if(this.isBoldCheck.isSelected()){\r
+            style = style | Font.BOLD;\r
+        }\r
+        if(this.isItalicCheck.isSelected()){\r
+            style = style | Font.ITALIC;\r
+        }\r
+        Font newFont = getSelectedFont();\r
+        if(newFont.getStyle() != style){\r
+            newFont = newFont.deriveFont(style);\r
+        }\r
+\r
+        AffineTransform tx = getFontRenderContext().getTransform();\r
+        boolean isAntiAliases = this.useTextAntiAliaseCheck.isSelected();\r
+        boolean useFractional = this.useFractionalCheck    .isSelected();\r
+        FontRenderContext newContext =\r
+                new FontRenderContext(tx, isAntiAliases, useFractional);\r
+\r
+        FontInfo newInfo = new FontInfo(newFont, newContext);\r
+        setFontInfo(newInfo);\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * フォントサイズ変更処理。\r
+     */\r
+    private void actionFontSizeSelected(){\r
+        Object selected = this.sizeSelector.getSelectedItem();\r
+        if(selected == null) return;\r
+\r
+        Integer selectedInteger;\r
+        if(selected instanceof Integer){\r
+            selectedInteger = (Integer) selected;\r
+        }else{\r
+            try{\r
+                selectedInteger = Integer.valueOf(selected.toString());\r
+            }catch(NumberFormatException e){\r
+                selectedInteger = Integer.valueOf(\r
+                        this.lastFontInfo.getFont().getSize()\r
+                        );\r
+            }\r
+        }\r
+\r
+        if(selectedInteger.intValue() <= 0){\r
+            selectedInteger =\r
+                    Integer.valueOf(this.lastFontInfo.getFont().getSize());\r
+        }\r
+\r
+        float fontSize = selectedInteger.floatValue();\r
+        Font newFont = getSelectedFont().deriveFont(fontSize);\r
+        FontInfo newInfo = this.fontInfo.deriveFont(newFont);\r
+        setFontInfo(newInfo);\r
+\r
+        int sizeItems = this.sizeSelector.getItemCount();\r
+        for(int index = 0; index <= sizeItems - 1; index++){\r
+            Object sizeItem = this.sizeSelector.getItemAt(index);\r
+            if(sizeItem.equals(selectedInteger)){\r
+                this.sizeSelector.setSelectedIndex(index);\r
+                break;\r
+            }\r
+        }\r
+\r
+        updateControlls();\r
+        updatePreview();\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * ボタン操作及びフォントサイズ指定コンボボックス操作のリスナ。\r
+     * @param event 操作イベント {@inheritDoc}\r
+     */\r
+    public void actionPerformed(ActionEvent event){\r
+        if(this.maskListener) return;\r
+\r
+        String cmd = event.getActionCommand();\r
+        if(cmd.equals(ActionManager.CMD_FONTSIZESEL)){\r
+            actionFontSizeSelected();\r
+        }\r
+\r
+        Object source = event.getSource();\r
+        if(source == this.resetDefault){\r
+            setFontInfo(FontInfo.DEFAULT_FONTINFO);\r
+        }\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * フォントファミリリスト選択操作のリスナ。\r
+     * @param event 操作イベント {@inheritDoc}\r
+     */\r
+    public void valueChanged(ListSelectionEvent event){\r
+        if(this.maskListener) return;\r
+\r
+        if(event.getSource() != this.familySelector) return;\r
+        if(event.getValueIsAdjusting()) return;\r
+\r
+        Object selected = this.familySelector.getSelectedValue();\r
+        if(selected == null) return;\r
+\r
+        String familyName = selected.toString();\r
+        Font currentFont = getSelectedFont();\r
+        int style = currentFont.getStyle();\r
+        int size  = currentFont.getSize();\r
+\r
+        Font newFont = new Font(familyName, style, size);\r
+        FontInfo newInfo = this.fontInfo.deriveFont(newFont);\r
+\r
+        setFontInfo(newInfo);\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * フォントプレビュー画面用コンポーネント。\r
+     */\r
+    private static class FontPreview extends JComponent{\r
+\r
+        private static final int MARGIN = 5;\r
+\r
+        private final GlyphDraw draw;\r
+\r
+        private FontInfo fontInfo;\r
+\r
+        /**\r
+         * コンストラクタ。\r
+         * @param source 文字列\r
+         * @param fontInfo フォント設定\r
+         */\r
+        public FontPreview(CharSequence source,\r
+                             FontInfo fontInfo ){\r
+            super();\r
+\r
+            this.fontInfo = fontInfo;\r
+            this.draw = new GlyphDraw(source, this.fontInfo);\r
+            this.draw.setFontInfo(this.fontInfo);\r
+\r
+            this.draw.setPos(MARGIN, MARGIN);\r
+\r
+            this.draw.setColor(Color.BLACK);\r
+            setBackground(Color.WHITE);\r
+\r
+            updateBounds();\r
+\r
+            return;\r
+        }\r
+\r
+        /**\r
+         * サイズ更新。\r
+         */\r
+        private void updateBounds(){\r
+            Rectangle bounds = this.draw.setWidth(Integer.MAX_VALUE);\r
+            Dimension dimension = new Dimension(bounds.width  + MARGIN * 2,\r
+                                                bounds.height + MARGIN * 2 );\r
+            setPreferredSize(dimension);\r
+            revalidate();\r
+            repaint();\r
+            return;\r
+        }\r
+\r
+        /**\r
+         * フォント設定の変更。\r
+         * @param newFontInfo フォント設定\r
+         */\r
+        public void setFontInfo(FontInfo newFontInfo){\r
+            this.fontInfo = newFontInfo;\r
+            this.draw.setFontInfo(this.fontInfo);\r
+\r
+            updateBounds();\r
+\r
+            return;\r
+        }\r
+\r
+        /**\r
+         * {@inheritDoc}\r
+         * 文字列の描画。\r
+         * @param g {@inheritDoc}\r
+         */\r
+        @Override\r
+        public void paintComponent(Graphics g){\r
+            super.paintComponent(g);\r
+            Graphics2D g2d = (Graphics2D) g;\r
+            this.draw.paint(g2d);\r
+            return;\r
+        }\r
+    }\r
+\r
+}\r