View Javadoc

1   /*
2    *  XNap Commons
3    *
4    *  Copyright (C) 2005  Felix Berger
5    *
6    *  This library is free software; you can redistribute it and/or
7    *  modify it under the terms of the GNU Lesser General Public
8    *  License as published by the Free Software Foundation; either
9    *  version 2.1 of the License, or (at your option) any later version.
10   *
11   *  This library is distributed in the hope that it will be useful,
12   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   *  Lesser General Public License for more details.
15   *
16   *  You should have received a copy of the GNU Lesser General Public
17   *  License along with this library; if not, write to the Free Software
18   *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19   */
20  package org.xnap.commons.gui.completion;
21  
22  import static org.xnap.commons.gui.completion.CompletionModeFactory.I18N;
23  import java.awt.event.InputEvent;
24  import java.awt.event.KeyAdapter;
25  import java.awt.event.KeyEvent;
26  import java.awt.event.KeyListener;
27  import javax.swing.KeyStroke;
28  import org.apache.commons.logging.Log;
29  import org.apache.commons.logging.LogFactory;
30  import org.xnap.commons.gui.shortcut.DefaultShortcut;
31  import org.xnap.commons.gui.shortcut.ShortcutManager;
32  
33  
34  /***
35   * This completion mode mimics Emacs' <code>(dabbrev-expand)</code>.
36   * 
37   * The keys triggering the forward/backward completion can be configured
38   * in the {@link ShortcutManager} framework.
39   *
40   * @author Felix Berger
41   */
42  public class EmacsCompletionMode extends AbstractCompletionMode
43  {
44  	public static final DefaultShortcut SHORTCUT_FORWARD = new DefaultShortcut
45  		(KeyStroke.getKeyStroke(KeyEvent.VK_T, InputEvent.CTRL_MASK),
46  		 I18N.tr("Completion"), I18N.tr("Emacs Cycle Forward")); 
47  
48  	
49  	public static final DefaultShortcut SHORTCUT_BACKWARD = new DefaultShortcut
50  		(KeyStroke.getKeyStroke(KeyEvent.VK_R, InputEvent.CTRL_MASK),
51  		 I18N.tr("Completion"), I18N.tr("Emacs Cycle Backward")); 
52  		 
53      private static Log logger = LogFactory.getLog(EmacsCompletionMode.class);
54  
55      /***
56      * Holds the key listener used for receiving the mode's trigger key events.
57      */
58      private KeyListener listener = new KeyHandler();
59      /***
60       * The current set of possible completions for the input which can be
61       * traversed.
62       */
63      private Object[] matches = null;
64      /***
65       * The index of the currently shown completion.
66       */
67      private int index = 0;
68      /***
69       * The original string which triggered the completion.
70       */
71      private String orig = null;
72  
73      public void disable()
74      {
75          getTextComponent().removeKeyListener(listener);
76      }
77  
78      protected void enable()
79      {
80          getTextComponent().addKeyListener(listener);
81      }
82  
83      private Object[] getMatches()
84      {
85          int size = getModel().getSize();
86          Object[] matches = new Object[size];
87  
88          for (int i = 0; i < size; i++) {
89              matches[i] = getModel().getElementAt(i);
90          }
91  
92          return matches;
93      }
94  
95      private class KeyHandler extends KeyAdapter
96      {
97          public void keyPressed(KeyEvent e)
98          {
99              if (SHORTCUT_FORWARD.getKeyStroke() != null 
100 				&& SHORTCUT_FORWARD.getKeyStroke().equals
101 				(KeyStroke.getKeyStrokeForEvent(e))) {
102                 // scroll forwards
103                 if (matches != null) {
104                     if ((index + 1) == matches.length) {
105                         setText(orig);
106                         getTextComponent().getToolkit().beep();
107                         matches = null;
108                     }
109                     else {
110                         setText(matches[++index].toString());
111                     }
112                 }
113                 else if (getModel().complete(getText())) {
114                     orig = getText();
115                     matches = getMatches();
116                     index = 0;
117                     setText(matches[0].toString());
118                 }
119                 e.consume();
120             }
121             else if (SHORTCUT_BACKWARD.getKeyStroke() != null 
122 					 && SHORTCUT_BACKWARD.getKeyStroke().equals
123 					 (KeyStroke.getKeyStrokeForEvent(e))) {
124 				// scroll backwards
125 				if (matches != null) {
126 					if (index > 0) {
127 						setText(matches[--index].toString());
128 					}
129 					else {
130 						setText(orig);
131 						matches = null;
132 					}
133 				}
134 				e.consume();
135 			}
136 			else if (e.getKeyCode() != KeyEvent.VK_ALT 
137 					 && e.getKeyCode() != KeyEvent.VK_CONTROL
138 					 && e.getKeyCode() != KeyEvent.VK_SHIFT
139 					 && e.getKeyCode() != KeyEvent.VK_META) {
140 				matches = null;
141 			}
142 		}
143     }
144 }