テキスト操作

javax.swing.text.JTextComponentのサブクラスで使える操作を確認します。

コマンド

テキストコンポーネントがサポートするコマンドセットは、getActions() メソッドで取得できます。
この戻り値は、javax.swing.Actionの配列です。(実質的にはTextAction)
この配列内容を列挙、表示するためのソースコードを以下に示します。
Actionの配列の各要素は、Actionインターフェイスの実装済みオブジェクトになっています。
Actionインターフェイスは、ActionListenerンターフェイスを継承(extends)したもので、JTextComponentなどの機能設定に使われます。
つまり、列挙されたこれらの能力も持っていることになります。

import java.awt.*;
import javax.swing.*;

public class Test extends JFrame {
	JTextPane jtext = new JTextPane();
	
	Test() {//コンストラクタ
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.jtext.setPreferredSize(new Dimension(160,120));
		this.getContentPane().add(jtext, BorderLayout.CENTER);
		this.pack();
		this.setVisible(true);
		Action[] array = this.jtext.getActions();
		for(int i = 0; i < array.length; i++){
			String name = (String)array[i].getValue(Action.NAME);
			String sub = (String)array[i].getValue(Action.ACCELERATOR_KEY);
			System.out.println(name+":"+sub);
		}	
	}
	public static void main(String[] args) {
		new Test();
	}
}

 これが実行した時の画面で、Actionの配列の出力結果はコンソールにでます。
上記実行で得られるコンソール表示内容を示します。(jre 1.6.0_246を使用時)

set-read-only:null
selection-down:null
selection-begin-line:null
delete-previous-word:null
font-size-48:null
select-word:null
selection-page-right:null
font-family-Serif:null
select-all:null
font-size-8:null
selection-page-down:null
font-size-36:null
insert-content:null
left-justify:null
selection-previous-word:null
selection-page-up:null
toggle-componentOrientation:null
selection-end-word:null
insert-break:null
caret-end-word:null
center-justify:null
page-up:null
font-size-24:null
beep:null
font-family-Monospaced:null
selection-page-left:null
selection-begin-word:null
right-justify:null
delete-previous:null
caret-begin-line:null
font-underline:null
delete-next-word:null
font-size-18:null
font-size-16:null
selection-forward:null
caret-forward:null
font-size-14:null
font-size-12:null
font-bold:null
default-typed:null
font-size-10:null
font-family-SansSerif:null
cut-to-clipboard:null
select-line:null
caret-end-paragraph:null
selection-up:null
caret-begin:null
copy-to-clipboard:null
select-paragraph:null
font-italic:null
caret-up:null
selection-end:null
caret-next-word:null
caret-down:null
selection-next-word:null
delete-next:null
selection-backward:null
selection-end-line:null
caret-begin-paragraph:null
set-writable:null
selection-begin:null
page-down:null
caret-end:null
caret-backward:null
caret-end-line:null
unselect:null
paste-from-clipboard:null
insert-tab:null
dump-model:null
selection-end-paragraph:null
selection-begin-paragraph:null
caret-begin-word:null
caret-previous-word:null

つまり、上記であればJTextPaneには、以上のActionのイベント処理が登録されているわけです。
そして、JMenuItemのコンストラクタでは、このActionオブジェクトを引数にできます。 つまり、これらに関しては、特に処理を作らなくても、メニュー処理ができるということです。 以下に、その例を示します。 それは、clipboardに対するcut,copy,pasteの処理です。

上記コマンドを利用したメニュー

目標の例を示します。

このソースコードを以下に示します。

import java.awt.*;
import javax.swing.*;
import javax.swing.text.*;
import java.awt.event.*;

public class Test extends JFrame {
	JTextPane jtext = new JTextPane();
	
	Action getActionByName(String act_name){//エディタの既に使えるAction群より、引数の名前のActionを取得する。
	    Action[] array = jtext.getActions();
	    for (int i = 0; i < array.length; i++) {
	        String name = (String)array[i].getValue(Action.NAME);
	        if (name.equals(act_name)) return array[i];
	    }
	    return null;
	}

	Test() {//コンストラクタ
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.jtext.setPreferredSize(new Dimension(160,120));
		this.getContentPane().add(jtext, BorderLayout.CENTER);
		
		//メニュー作成
		JMenuBar menuBar = new javax.swing.JMenuBar();
		this.getRootPane().setJMenuBar(menuBar);//メニューバーのセット
		JMenu menuEdit = new JMenu("編集(E)");
		menuEdit.setMnemonic('E');
		menuBar.add(menuEdit);
		Action actionCut=getActionByName("cut-to-clipboard");
		JMenuItem itemCut = new JMenuItem(actionCut);
		menuEdit.add(itemCut);//メニュー項目追加
		Action actionCopy=getActionByName("copy-to-clipboard");
		JMenuItem itemCopy = new JMenuItem(actionCopy);
		menuEdit.add(itemCopy);//メニュー項目追加
		Action actionPaste=getActionByName("paste-from-clipboard");
		//actionPaste.putValue(Action.NAME, "貼り付け");
		JMenuItem itemPaste = new JMenuItem(actionPaste);
		//itemPaste.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_V, InputEvent.CTRL_DOWN_MASK));
		menuEdit.add(itemPaste);//メニュー項目追加
		
		this.pack();
		this.setVisible(true);
	}
	public static void main(String[] args) {
		new Test();
	}
}

上記コメントのactionPaste.putValue(Action.NAME, "貼り付け"); を使うことで、メニューの表示内容を次のように変更できます。

これはす既にアクセラレータ(メニューショートカット機能)が設定されていますが、 メニューで分かるように表示されていません。

上記コメントの
itemPaste.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_V, InputEvent.CTRL_DOWN_MASK)); を使うことで、メニューにショートカットを表示できます。

なお、Ctrl-Vは上記の代わりに次のような表現を使っても可能で、まったく同じです。
itemPaste.setAccelerator(KeyStroke.getKeyStroke("ctrl V"));
ここで使っているKeyStrokeクラスは、イベントのキー操作を一意的に指定するため使うデータを staticで付けているクラスで、キー操作イベントを作る時に使います。 実際には、使うキー操作からgetKeyStrokeで、この一意的データ取得して使う形態です。

メニュー操作と別で、キーストロークの処理にバインドする手法 その1
InputMapを使う

Ctrlを押しながらBキーを押した時に、キャレットを1文字バックさせる操作です。 ですがActionの処理自体は、DefaultEditorKitのbackwardActionオブジェクトを利用しています。

import java.awt.*;
import javax.swing.*;
import javax.swing.text.*;
import java.awt.event.*;

public class Test extends JFrame {
	JTextPane jtext = new JTextPane();
	
	Test() {//コンストラクタ
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.jtext.setPreferredSize(new Dimension(160,120));
		this.getContentPane().add(jtext, BorderLayout.CENTER);
		
		//Ctrlを押しながらBキーを押した時に、キャレットを1文字バックさせる
		InputMap inputMap = jtext.getInputMap();
		KeyStroke key = KeyStroke.getKeyStroke(KeyEvent.VK_B, Event.CTRL_MASK);
		inputMap.put(key, DefaultEditorKit.backwardAction);
		
		this.pack();
		this.setVisible(true);
	}
	public static void main(String[] args) {
		new Test();
	}
}

メニュー操作と別で、キーストロークの処理にバインドする手法 その2
keymapを使う

キーマップを変更することで、そのキーマップ内にあるキーストローク の処理を変更できます。以下では、JTextComponent.loadKeymapメソッドで、 引数のキーマップ内に、複数のキーストロークに対するアクションを設定する例として、 切り取り処理に、Ctrl+Cを使い、 コピー処理に、Ctrl+Rを使い、 貼り付け処理に、Ctrl+Enterのキーストロークを使う設定を行っている例です。

import java.awt.*;
import javax.swing.*;
import javax.swing.text.*;
import java.awt.event.*;

public class Test extends JFrame {
	JTextPane jtext = new JTextPane();
	
	Test() {//コンストラクタ
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.jtext.setPreferredSize(new Dimension(160,120));
		this.getContentPane().add(jtext, BorderLayout.CENTER);

		JTextComponent.KeyBinding []defaultBindings = {
			new JTextComponent.KeyBinding(
					KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.CTRL_MASK),
					DefaultEditorKit.cutAction
			),
			new JTextComponent.KeyBinding(
					KeyStroke.getKeyStroke(KeyEvent.VK_R, InputEvent.CTRL_MASK),
					DefaultEditorKit.copyAction
			),
			new JTextComponent.KeyBinding(
					KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, InputEvent.CTRL_MASK),
					DefaultEditorKit.pasteAction
			),
		};
		Keymap keymap = jtext.getKeymap();
		JTextComponent.loadKeymap(keymap, defaultBindings, jtext.getActions());
		Action[] array = keymap.getBoundActions();//設定の確認表示
		for(int i = 0; i < array.length; i++){
			String name = (String)array[i].getValue(Action.NAME);
			String sub = (String)array[i].getValue(Action.ACCELERATOR_KEY);
			System.out.println(name+":"+sub);
		}	
		
		this.pack();
		this.setVisible(true);
	}
	public static void main(String[] args) {
		new Test();
	}
}
cut-to-clipboard:null
copy-to-clipboard:null
paste-from-clipboard:null

実行で、コンソールに キーマップkeymap内で使っているアクションの名前を上記のように列挙されますが、 このJTextComponent.loadKeymapの実行前で行うと、 なにも表示しません。 つまり、loadKeymapこの実行で、新規設定されていることが確認できます。
なお上記では、変更していない例えばCtrl-XCtrl-Vの処理は、 jtextのコマンドリストに登録させているので、これまで通りに使えます。

keymapに新しい処理(TextActionのサブクラス)を設定する。

Keymap keymap = jtext.getKeymap();で、取得したkeymapにaddActionForKeyStrokeで、 指定のストロークに対するActionオブジェクトを追加します。
ここで指定するActionオブジェクトは、 TextActionのサブクラスをTestTextActionの名前で作って そのオブジェクトを指定しています。 そのactionPerformedをオーバーライドしたところにキーストロークの処理を書きます。
以下では、Enterキーのストロークに対して、"<br>\n"の文字列を挿入する処理にしています。

import java.awt.*;
import javax.swing.*;
import javax.swing.text.*;
import java.awt.event.*;

public class Test extends JFrame {
	JTextPane jtext = new JTextPane();

	class TestTextAction extends TextAction{
		public TestTextAction(String str){
				super(str);
		}
		public void actionPerformed(ActionEvent e){
			JTextComponent textcmp =  (JTextComponent)getFocusedComponent();
			Document doc = textcmp.getDocument();
			try{
				doc.insertString(textcmp.getCaretPosition(),"<br>\n",null);
			}
			catch(Exception err){ err.printStackTrace(); }	
		}
	}

	Test() {//コンストラクタ
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.jtext.setPreferredSize(new Dimension(160,120));
		this.getContentPane().add(jtext, BorderLayout.CENTER);

		Keymap keymap = jtext.getKeymap();
		keymap.addActionForKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,0),
								new TestTextAction("test-action"));
		
		Action[] array = keymap.getBoundActions();//設定の確認表示
		for(int i = 0; i < array.length; i++){
			String name = (String)array[i].getValue(Action.NAME);
			String sub = (String)array[i].getValue(Action.ACCELERATOR_KEY);
			System.out.println(name+":"+sub);
		}	
		
		this.pack();
		this.setVisible(true);
	}
	public static void main(String[] args) {
		new Test();
	}
}

下に確認用のコンソール表示内容も示します

test-action:null