OSDN Git Service

Merge commit '2458eff3aea04f67893bc824b5cf896fbb767332'
[jindolf/Jindolf.git] / src / main / java / jp / sourceforge / jindolf / TalkPreview.java
diff --git a/src/main/java/jp/sourceforge/jindolf/TalkPreview.java b/src/main/java/jp/sourceforge/jindolf/TalkPreview.java
new file mode 100644 (file)
index 0000000..b00973a
--- /dev/null
@@ -0,0 +1,505 @@
+/*\r
+ * 発言エディットパネル\r
+ *\r
+ * Copyright(c) 2008 olyutorskii\r
+ * $Id: TalkPreview.java 953 2009-12-06 16:42:14Z 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.Font;\r
+import java.awt.GridBagConstraints;\r
+import java.awt.GridBagLayout;\r
+import java.awt.Insets;\r
+import java.awt.LayoutManager;\r
+import java.awt.event.ActionEvent;\r
+import java.awt.event.ActionListener;\r
+import java.io.File;\r
+import javax.swing.BorderFactory;\r
+import javax.swing.JButton;\r
+import javax.swing.JComponent;\r
+import javax.swing.JFrame;\r
+import javax.swing.JLabel;\r
+import javax.swing.JOptionPane;\r
+import javax.swing.JPanel;\r
+import javax.swing.JPopupMenu;\r
+import javax.swing.JScrollPane;\r
+import javax.swing.JSplitPane;\r
+import javax.swing.JViewport;\r
+import javax.swing.border.BevelBorder;\r
+import javax.swing.border.Border;\r
+import javax.swing.border.TitledBorder;\r
+import javax.swing.event.ChangeEvent;\r
+import javax.swing.event.ChangeListener;\r
+import javax.swing.text.JTextComponent;\r
+import jp.sourceforge.jindolf.json.JsArray;\r
+import jp.sourceforge.jindolf.json.JsObject;\r
+import jp.sourceforge.jindolf.json.JsString;\r
+import jp.sourceforge.jindolf.json.JsValue;\r
+\r
+/**\r
+ * 発言エディットパネル。\r
+ */\r
+@SuppressWarnings("serial")\r
+public class TalkPreview extends JFrame\r
+        implements ActionListener, ChangeListener{\r
+\r
+    private static final String FRAMETITLE = "発言エディタ - " + Jindolf.TITLE;\r
+    private static final Color COLOR_EDITORBACK = Color.BLACK;\r
+    private static final String DRAFT_FILE = "draft.json";\r
+\r
+    private final JTextComponent freeMemo = new TextEditor();\r
+\r
+    private final EditArray editArray = new EditArray();\r
+\r
+    private final JButton cutButton      = new JButton("カット");\r
+    private final JButton copyButton     = new JButton("コピー");\r
+    private final JButton clearButton    = new JButton("クリア");\r
+    private final JButton cutAllButton   = new JButton("全カット");\r
+    private final JButton copyAllButton  = new JButton("全コピー");\r
+    private final JButton clearAllButton = new JButton("全クリア");\r
+    private final JButton closeButton    = new JButton("閉じる");\r
+    private final TitledBorder numberBorder =\r
+            BorderFactory.createTitledBorder("");\r
+    private final JComponent singleGroup = buildSingleGroup();\r
+    private final JComponent multiGroup  = buildMultiGroup();\r
+    private final JLabel letsBrowser =\r
+            new JLabel("投稿はWebブラウザからどうぞ");\r
+\r
+    private JsObject loadedDraft = null;\r
+\r
+    /**\r
+     * コンストラクタ。\r
+     */\r
+    public TalkPreview(){\r
+        super(FRAMETITLE);\r
+\r
+        GUIUtils.modifyWindowAttributes(this, true, false, true);\r
+\r
+        setDefaultCloseOperation(HIDE_ON_CLOSE);\r
+\r
+        this.cutButton      .addActionListener(this);\r
+        this.copyButton     .addActionListener(this);\r
+        this.clearButton    .addActionListener(this);\r
+        this.cutAllButton   .addActionListener(this);\r
+        this.copyAllButton  .addActionListener(this);\r
+        this.clearAllButton .addActionListener(this);\r
+        this.closeButton    .addActionListener(this);\r
+\r
+        this.editArray.addChangeListener(this);\r
+\r
+        Container content = getContentPane();\r
+        design(content);\r
+\r
+        setBorderNumber(1);\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * レイアウトを行う。\r
+     * @param content コンテナ\r
+     */\r
+    private void design(Container content){\r
+        JComponent freeNotePanel = buildFreeNotePanel();\r
+\r
+        JScrollPane scrollPane  = new JScrollPane();\r
+        scrollPane.setHorizontalScrollBarPolicy(\r
+                JScrollPane.HORIZONTAL_SCROLLBAR_NEVER\r
+        );\r
+        JViewport viewPort = new JViewport();\r
+        viewPort.setBackground(COLOR_EDITORBACK);\r
+        viewPort.setView(this.editArray);\r
+        scrollPane.setViewport(viewPort);\r
+\r
+        LayoutManager layout;\r
+        Border border;\r
+\r
+        JComponent editPanel = new JPanel();\r
+        layout = new BorderLayout();\r
+        editPanel.setLayout(layout);\r
+        editPanel.add(scrollPane, BorderLayout.CENTER);\r
+        JComponent buttonPanel = buildButtonPanel();\r
+        editPanel.add(buttonPanel, BorderLayout.EAST);\r
+        border = BorderFactory.createTitledBorder("発言編集");\r
+        editPanel.setBorder(border);\r
+\r
+        JSplitPane split = new JSplitPane();\r
+        split.setOrientation(JSplitPane.HORIZONTAL_SPLIT);\r
+        split.setContinuousLayout(false);\r
+        split.setDividerSize(10);\r
+        split.setDividerLocation(200);\r
+        split.setOneTouchExpandable(true);\r
+        split.setLeftComponent(freeNotePanel);\r
+        split.setRightComponent(editPanel);\r
+\r
+        Border inside  = BorderFactory.createBevelBorder(BevelBorder.LOWERED);\r
+        Border outside = BorderFactory.createEmptyBorder(2, 5, 2, 2);\r
+        border = BorderFactory.createCompoundBorder(inside, outside);\r
+        this.letsBrowser.setBorder(border);\r
+\r
+        layout = new BorderLayout();\r
+        content.setLayout(layout);\r
+        content.add(split, BorderLayout.CENTER);\r
+        content.add(this.letsBrowser, BorderLayout.SOUTH);\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * ボタン群を生成する。\r
+     * @return ボタン群\r
+     */\r
+    private JComponent buildButtonPanel(){\r
+        JPanel panel = new JPanel();\r
+\r
+        LayoutManager layout = new GridBagLayout();\r
+        GridBagConstraints constraints = new GridBagConstraints();\r
+\r
+        panel.setLayout(layout);\r
+\r
+        constraints.weightx = 1.0;\r
+        constraints.weighty = 0.0;\r
+        constraints.fill = GridBagConstraints.NONE;\r
+        constraints.anchor = GridBagConstraints.WEST;\r
+        constraints.gridx = 1;\r
+        constraints.gridy = GridBagConstraints.RELATIVE;\r
+        constraints.gridwidth = 1;\r
+        constraints.gridheight = 1;\r
+\r
+\r
+        constraints.insets = new Insets(3, 3, 3, 3);\r
+        panel.add(this.singleGroup, constraints);\r
+\r
+        constraints.insets = new Insets(10, 3, 3, 3);\r
+        panel.add(this.multiGroup, constraints);\r
+\r
+        constraints.weighty = 1.0;\r
+        constraints.anchor = GridBagConstraints.SOUTH;\r
+        constraints.insets = new Insets(3, 3, 10, 3);\r
+        panel.add(this.closeButton, constraints);\r
+\r
+        return panel;\r
+    }\r
+\r
+    /**\r
+     * アクティブ発言操作ボタン群を生成する。\r
+     * @return ボタン群\r
+     */\r
+    private JComponent buildSingleGroup(){\r
+        JComponent panel = new JPanel();\r
+\r
+        LayoutManager layout = new GridBagLayout();\r
+        GridBagConstraints constraints = new GridBagConstraints();\r
+\r
+        panel.setLayout(layout);\r
+\r
+        constraints.weightx = 1.0;\r
+        constraints.weighty = 0.0;\r
+        constraints.fill = GridBagConstraints.HORIZONTAL;\r
+        constraints.gridx = 1;\r
+        constraints.gridy = GridBagConstraints.RELATIVE;\r
+        constraints.gridwidth = 1;\r
+        constraints.gridheight = 1;\r
+        constraints.insets = new Insets(3, 3, 3, 3);\r
+\r
+        panel.add(this.cutButton,   constraints);\r
+        panel.add(this.copyButton,  constraints);\r
+        panel.add(this.clearButton, constraints);\r
+\r
+        panel.setBorder(this.numberBorder);\r
+\r
+        return panel;\r
+    }\r
+\r
+    /**\r
+     * 全発言操作ボタン群を生成する。\r
+     * @return ボタン群\r
+     */\r
+    private JComponent buildMultiGroup(){\r
+        JComponent panel = new JPanel();\r
+\r
+        LayoutManager layout = new GridBagLayout();\r
+        GridBagConstraints constraints = new GridBagConstraints();\r
+\r
+        panel.setLayout(layout);\r
+\r
+        constraints.weightx = 1.0;\r
+        constraints.weighty = 0.0;\r
+        constraints.fill = GridBagConstraints.HORIZONTAL;\r
+        constraints.gridx = 1;\r
+        constraints.gridy = GridBagConstraints.RELATIVE;\r
+        constraints.gridwidth = 1;\r
+        constraints.gridheight = 1;\r
+        constraints.insets = new Insets(3, 3, 3, 3);\r
+\r
+        panel.add(this.cutAllButton,   constraints);\r
+        panel.add(this.copyAllButton,  constraints);\r
+        panel.add(this.clearAllButton, constraints);\r
+\r
+        Border border = BorderFactory.createTitledBorder("全発言を");\r
+        panel.setBorder(border);\r
+\r
+        return panel;\r
+    }\r
+\r
+    /**\r
+     * フリーノート部を生成する。\r
+     * @return フリーノート部\r
+     */\r
+    private JComponent buildFreeNotePanel(){\r
+        Insets margin = new Insets(3, 3, 3, 3);\r
+        this.freeMemo.setMargin(margin);\r
+        JPopupMenu popup = new TextPopup();\r
+        this.freeMemo.setComponentPopupMenu(popup);\r
+\r
+        JScrollPane scrollPane = new JScrollPane();\r
+        scrollPane.setHorizontalScrollBarPolicy(\r
+                JScrollPane.HORIZONTAL_SCROLLBAR_NEVER\r
+        );\r
+        JViewport viewPort = new JViewport();\r
+        viewPort.setView(this.freeMemo);\r
+        scrollPane.setViewport(viewPort);\r
+\r
+        JComponent panel = new JPanel();\r
+\r
+        LayoutManager layout = new GridBagLayout();\r
+        GridBagConstraints constraints = new GridBagConstraints();\r
+\r
+        panel.setLayout(layout);\r
+\r
+        constraints.weightx = 1.0;\r
+        constraints.weighty = 1.0;\r
+        constraints.fill = GridBagConstraints.BOTH;\r
+        constraints.insets = new Insets(1, 1, 1, 1);\r
+        panel.add(scrollPane, constraints);\r
+\r
+        Border border = BorderFactory.createTitledBorder("フリーメモ");\r
+        panel.setBorder(border);\r
+\r
+        return panel;\r
+    }\r
+\r
+    /**\r
+     * アクティブ発言の通し番号表示を更新。\r
+     * @param num 通し番号\r
+     */\r
+    private void setBorderNumber(int num){\r
+        String title = "発言#"+num+" を";\r
+        this.numberBorder.setTitle(title);\r
+        this.singleGroup.revalidate();\r
+        this.singleGroup.repaint();\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * テキスト編集用フォントを指定する。\r
+     * 描画属性は無視される。\r
+     * @param fontInfo フォント設定\r
+     */\r
+    public void setFontInfo(FontInfo fontInfo){\r
+        setTextFont(fontInfo.getFont());\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * テキスト編集用フォントを指定する。\r
+     * @param textFont フォント\r
+     */\r
+    public void setTextFont(Font textFont){\r
+        this.freeMemo.setFont(textFont);\r
+        this.editArray.setTextFont(textFont);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * テキスト編集用フォントを取得する。\r
+     * @return フォント\r
+     */\r
+    public Font getTextFont(){\r
+        return this.editArray.getTextFont();\r
+    }\r
+\r
+    /**\r
+     * 発言クリア操作の確認ダイアログを表示する。\r
+     * @return OKなら0, Cancelなら2\r
+     */\r
+    private int warnClear(){\r
+        int result = JOptionPane.showConfirmDialog(\r
+                this,\r
+                "本当に発言をクリアしてもよいですか?",\r
+                "発言クリア確認",\r
+                JOptionPane.OK_CANCEL_OPTION,\r
+                JOptionPane.QUESTION_MESSAGE );\r
+        return result;\r
+    }\r
+\r
+    /**\r
+     * 原稿のロード。\r
+     */\r
+    public void loadDraft(){\r
+        JsValue value = ConfigFile.loadJson(new File(DRAFT_FILE));\r
+        if(value == null) return;\r
+\r
+        if( ! (value instanceof JsObject) ) return;\r
+        JsObject root = (JsObject) value;\r
+\r
+        value = root.getValue("freeMemo");\r
+        if(value instanceof JsString){\r
+            JsString memo = (JsString) value;\r
+            this.freeMemo.setText(memo.toRawString());\r
+        }\r
+\r
+        value = root.getValue("drafts");\r
+        if( ! (value instanceof JsArray) ) return;\r
+        JsArray array = (JsArray) value;\r
+\r
+        StringBuilder draftAll = new StringBuilder();\r
+        for(JsValue elem : array){\r
+            if( ! (elem instanceof JsString) ) continue;\r
+            JsString draft = (JsString) elem;\r
+            draftAll.append(draft.toRawString());\r
+        }\r
+        this.editArray.clearAllEditor();\r
+        this.editArray.setAllText(draftAll);\r
+\r
+        this.loadedDraft = root;\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 原稿のセーブ。\r
+     */\r
+    public void saveDraft(){\r
+        AppSetting setting = Jindolf.getAppSetting();\r
+        if( ! setting.useConfigPath() ) return;\r
+        File configPath = setting.getConfigPath();\r
+        if(configPath == null) return;\r
+\r
+        JsObject root = new JsObject();\r
+        JsString memo = new JsString(this.freeMemo.getText());\r
+        root.putValue("freeMemo", memo);\r
+\r
+        JsArray array = new JsArray();\r
+        JsString text = new JsString(this.editArray.getAllText());\r
+        array.add(text);\r
+        root.putValue("drafts", array);\r
+\r
+        if(this.loadedDraft != null){\r
+            if(this.loadedDraft.equals(root)) return;\r
+        }\r
+\r
+        ConfigFile.saveJson(new File(DRAFT_FILE), root);\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * アクティブな発言をカットしクリップボードへコピーする。\r
+     */\r
+    private void actionCutActive(){\r
+        actionCopyActive();\r
+        actionClearActive(false);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * アクティブな発言をクリップボードにコピーする。\r
+     */\r
+    private void actionCopyActive(){\r
+        TalkEditor activeEditor = this.editArray.getActiveEditor();\r
+        if(activeEditor == null) return;\r
+\r
+        CharSequence text = activeEditor.getText();\r
+        ClipboardAction.copyToClipboard(text);\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * アクティブな発言をクリアする。\r
+     * @param confirm trueなら確認ダイアログを出す\r
+     */\r
+    private void actionClearActive(boolean confirm){\r
+        if(confirm && warnClear() != 0 ) return;\r
+\r
+        TalkEditor activeEditor = this.editArray.getActiveEditor();\r
+        if(activeEditor == null) return;\r
+\r
+        activeEditor.clearText();\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 全発言をカットしクリップボードへコピーする。\r
+     */\r
+    private void actionCutAll(){\r
+        actionCopyAll();\r
+        actionClearAll(false);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 全発言をクリップボードにコピーする。\r
+     */\r
+    private void actionCopyAll(){\r
+        CharSequence text = this.editArray.getAllText();\r
+        ClipboardAction.copyToClipboard(text);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 全発言をクリアする。\r
+     * @param confirm trueなら確認ダイアログを出す\r
+     */\r
+    private void actionClearAll(boolean confirm){\r
+        if(confirm && warnClear() != 0 ) return;\r
+        this.editArray.clearAllEditor();\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * 上位ウィンドウをクローズする。\r
+     */\r
+    private void actionClose(){\r
+        setVisible(false);\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * {@inheritDoc}\r
+     * 各種ボタン操作の処理。\r
+     * @param event {@inheritDoc}\r
+     */\r
+    public void actionPerformed(ActionEvent event){\r
+        Object source = event.getSource();\r
+        if     (source == this.cutButton)      actionCutActive();\r
+        else if(source == this.copyButton)     actionCopyActive();\r
+        else if(source == this.clearButton)    actionClearActive(true);\r
+        else if(source == this.cutAllButton)   actionCutAll();\r
+        else if(source == this.copyAllButton)  actionCopyAll();\r
+        else if(source == this.clearAllButton) actionClearAll(true);\r
+        else if(source == this.closeButton)    actionClose();\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * アクティブなエディタが変更された時の処理。\r
+     * @param event イベント情報\r
+     */\r
+    public void stateChanged(ChangeEvent event){\r
+        TalkEditor activeEditor = this.editArray.getActiveEditor();\r
+        int seqNo = activeEditor.getSequenceNumber();\r
+        setBorderNumber(seqNo);\r
+        return;\r
+    }\r
+\r
+    // TODO アンドゥ・リドゥ機能\r
+    // TODO バルーンの雰囲気を選択できるようにしたい。(白、赤、青、灰)\r
+    // TODO アンカーの表記揺れの指摘\r
+}\r