--- /dev/null
+/*\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