001 /*
002 * Copyright (C) 2003 Adam Olsen
003 *
004 * This program is free software; you can redistribute it and/or modify it under
005 * the terms of the GNU General Public License as published by the Free Software
006 * Foundation; either version 1, or (at your option) any later version.
007 *
008 * This program is distributed in the hope that it will be useful, but WITHOUT
009 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
010 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
011 * details.
012 *
013 * You should have received a copy of the GNU General Public License along with
014 * this program; if not, write to the Free Software Foundation, Inc., 675 Mass
015 * Ave, Cambridge, MA 02139, USA.
016 */
017
018 package com.valhalla.gui;
019
020 import javax.swing.*;
021 import java.awt.event.*;
022 import javax.swing.event.*;
023 import java.util.*;
024
025 /**
026 * A JTextArea with a copy/paste context menu. Also includes an
027 * optional history manager - IE, pressing the up and down arrows
028 * will bring up different history items
029 *
030 * @author Adam Olsen
031 **/
032 public class MJTextArea extends JTextArea
033 {
034 private Vector history = new Vector();
035 private int currentLine = 0;
036
037 boolean h = false;
038 public MJTextArea(boolean history, int rows, int cols)
039 {
040 super(rows, cols);
041 this.h = history;
042 if(h) addKeyListener(new ArrowListener());
043 CopyPasteContextMenu.registerComponent(this);
044 initializeKeyboardBindings();
045 }
046
047 public MJTextArea( boolean history )
048 {
049 this(history, 0, 0);
050 }
051
052 public MJTextArea()
053 {
054 this(false, 0,0);
055 }
056
057 public MJTextArea(int rows, int cols)
058 {
059 this(false, rows, cols);
060 }
061
062 void clearText()
063 {
064 super.setText("");
065 }
066
067 public void setText(String text)
068 {
069 if( h )
070 {
071 checkHistoryAdd(getText());
072 }
073 super.setText(text);
074 }
075
076 /**
077 * Sets the text without affecting the history
078 **/
079 public void setNewText(String text)
080 {
081 super.setText(text);
082 }
083
084 /**
085 * adds keyboard action to this MJTextArea
086 * @param aKeyEventAction to add
087 */
088 public void addKeyboardBindingAction(KeyBindingAction aKeyEventAction)
089 {
090 getInputMap().put(KeyStroke.getKeyStroke(aKeyEventAction.getKeyEvent(),
091 aKeyEventAction.getKeyModifier()), aKeyEventAction.getName());
092 getActionMap().put(aKeyEventAction.getName(), aKeyEventAction);
093 }
094
095 /**
096 * initialization of "default" keybindings. As default JTextArea that MJTextArea inherits
097 * from does not contain all handy shortcuts, they can be added here
098 */
099 private void initializeKeyboardBindings()
100 {
101 // add Ctrl-Backspace binding to delete the (rest) of current word
102 addKeyboardBindingAction(new KeyBindingAction("ctrlBackspace",
103 java.awt.event.KeyEvent.VK_BACK_SPACE,
104 java.awt.Event.CTRL_MASK)
105 {
106 public void actionPerformed(ActionEvent e)
107 {
108 int caretPosition = getCaretPosition();
109 String contents = getText();
110 if(contents.length() == 0) return;
111 String stringBeforeCaret = contents.substring(0,caretPosition - 1);
112 String stringAfterCaret = contents.substring(caretPosition,contents.length());
113
114 int lastSpaceIndex = stringBeforeCaret.lastIndexOf(' ');
115 int lastNLIndex = stringBeforeCaret.lastIndexOf('\n');
116
117 int lastIndex = (lastSpaceIndex > lastNLIndex ? lastSpaceIndex : lastNLIndex);
118 String modifiedStringBeforeCaret = stringBeforeCaret.substring(0, lastIndex + 1 );
119 int newCaretPos = modifiedStringBeforeCaret.length();
120 setText(modifiedStringBeforeCaret + stringAfterCaret);
121 setCaretPosition(newCaretPos);
122 repaint();
123 }
124 });
125 }
126
127 /**
128 * simple abstract class to encapsulate key bindings and their actions. The reason for it is
129 * to have all the information on key binding: key event, modifier and the action in one place
130 */
131 abstract class KeyBindingAction extends AbstractAction
132 {
133 private int keyEvent;
134 private int keyModifier;
135
136 public KeyBindingAction(String name, int aKeyEvent, int aKeyModifier)
137 {
138 super(name);
139 keyEvent = aKeyEvent;
140 keyModifier = aKeyModifier;
141 }
142
143 public int getKeyEvent()
144 {
145 return keyEvent;
146 }
147
148 public int getKeyModifier()
149 {
150 return keyModifier;
151 }
152
153 public String getName()
154 {
155 return (String) getValue(Action.NAME);
156 }
157 }
158
159 boolean checkHistoryAdd(String text)
160 {
161 if(text.trim().equals(""))
162 {
163 return false;
164 }
165 currentLine = history.size();
166 if( history.size() != 0 )
167 {
168 String last = (String)history.get(history.size() - 1);
169 if(last.equals(text)) return false;
170 }
171
172 history.add(text);
173 currentLine = history.size();
174
175 return true;
176 }
177
178 /**
179 * Description of the Class
180 *
181 *@author synic
182 *@created September 9, 2005
183 */
184 class ArrowListener extends KeyAdapter {
185 /**
186 * Description of the Method
187 *
188 *@param e Description of the Parameter
189 */
190 public void keyPressed(KeyEvent e) {
191 if ( (e.getKeyCode() == KeyEvent.VK_DOWN || e.getKeyCode() == KeyEvent.VK_UP )
192 && e.isControlDown()) {
193
194 if(e.getKeyCode() == KeyEvent.VK_UP && currentLine >= history.size())
195 {
196 if(checkHistoryAdd(getText())) currentLine--;
197 }
198
199 if(e.getKeyCode() == KeyEvent.VK_DOWN && currentLine >= history.size())
200 {
201 checkHistoryAdd(getText());
202 }
203
204 e.consume();
205 int add = 0;
206 if(e.getKeyCode() == KeyEvent.VK_DOWN) add++;
207 else add--;
208
209 currentLine += add;
210 if( currentLine >= history.size())
211 {
212 clearText();
213 currentLine = history.size();
214 return;
215 }
216
217 if( currentLine < 0 )
218 {
219 currentLine = 0;
220 }
221
222 String t = (String)history.get(currentLine);
223 setNewText(t.replaceAll("\n$", ""));
224 }
225 }
226 }
227 }