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    }