001/**
002 *                    BioJava development code
003 *
004 * This code may be freely distributed and modified under the
005 * terms of the GNU Lesser General Public Licence.  This should
006 * be distributed with the code.  If you do not have a copy,
007 * see:
008 *
009 *      http://www.gnu.org/copyleft/lesser.html
010 *
011 * Copyright for this code is held jointly by the individual
012 * authors.  These should be listed in @author doc comments.
013 *
014 * For more information on the BioJava project and its aims,
015 * or to join the biojava-l mailing list, visit the home page
016 * at:
017 *
018 *      http://www.biojava.org/
019 *
020 * Created on Sep 14, 2011
021 * Created by Andreas Prlic
022 *
023 * @since 3.0.2
024 */
025package org.biojava.nbio.structure.align.gui.autosuggest;
026
027import javax.swing.*;
028import java.awt.*;
029import java.awt.event.*;
030import java.util.Vector;
031import java.util.concurrent.atomic.AtomicBoolean;
032
033
034
035
036/** A JTextField that can make suggestions for auto-complete.
037 *
038 * @author Andreas Prlic
039 *
040 */
041public class JAutoSuggest extends JTextField{
042
043        /**
044         *
045         */
046        private static final long serialVersionUID = 8591734727984365156L;
047
048        private static final String DEFAULT_TEXT= "Please enter text ...";
049
050        String defaultText;
051        private JDialog dialog;
052        private Point location;
053        private JList list;
054
055        private Vector<String> suggestions;
056
057        /** last word that was entered by user */
058        private String lastWord ;
059
060        AutoSuggestProvider autoSuggestProvider;
061
062        Font regular;
063        Font busy;
064
065        SuggestionFetcher matcher;
066
067        public JAutoSuggest(){
068                super();
069
070                init();
071        }
072
073        public JAutoSuggest(int size){
074                super(size);
075                init();
076        }
077
078        public JAutoSuggest(Frame owner){
079                owner.addComponentListener(new ComponentListener() {
080                        @Override
081                        public void componentShown(ComponentEvent e) {
082                                updateLocation();
083                        }
084
085                        @Override
086                        public void componentResized(ComponentEvent e) {
087                                updateLocation();
088                        }
089
090                        @Override
091                        public void componentMoved(ComponentEvent e) {
092                                updateLocation();
093                        }
094
095                        @Override
096                        public void componentHidden(ComponentEvent e) {
097                                updateLocation();
098                        }
099                });
100                owner.addWindowListener(new WindowListener() {
101                        @Override
102                        public void windowOpened(WindowEvent e) {
103                        }
104
105                        @Override
106                        public void windowIconified(WindowEvent e) {
107                                dialog.setVisible(false);
108                        }
109
110                        @Override
111                        public void windowDeiconified(WindowEvent e) {
112                        }
113
114                        @Override
115                        public void windowDeactivated(WindowEvent e) {
116                        }
117
118                        @Override
119                        public void windowClosing(WindowEvent e) {
120                                dialog.dispose();
121                        }
122
123                        @Override
124                        public void windowClosed(WindowEvent e) {
125                                dialog.dispose();
126                        }
127
128                        @Override
129                        public void windowActivated(WindowEvent e) {
130                        }
131                });
132                addFocusListener(new FocusListener() {
133                        @Override
134                        public void focusLost(FocusEvent e) {
135                                System.out.println("Lost Focus");
136                                dialog.setVisible(false);
137
138                                if (getText().trim().equals("") && e.getOppositeComponent() != null && e.getOppositeComponent().getName() != null) {
139                                        if (!e.getOppositeComponent().getName().equals("suggestFieldDropdownButton")) {
140                                                setText(defaultText);
141                                        }
142                                } else if (getText().trim().equals("")) {
143                                        setText(defaultText);
144                                }
145                        }
146
147                        @Override
148                        public void focusGained(FocusEvent e) {
149                                System.out.println("Lost Gained");
150                                if (getText().trim().equals(defaultText)) {
151                                        setText("");
152                                }
153
154                                showSuggest();
155                        }
156                });
157
158
159
160
161                init();
162
163                // set dialog owner...
164                //dialog.setD
165
166        }
167
168
169        private void initSuggestionList() {
170                list = new JList();
171                list.addMouseListener(new MouseListener() {
172                        private int selected;
173
174                        @Override
175                        public void mousePressed(MouseEvent e) {
176                        }
177
178                        @Override
179                        public void mouseReleased(MouseEvent e) {
180                                if (selected == list.getSelectedIndex()) {
181                                        // provide double-click for selecting a suggestion
182                                        setText((String) list.getSelectedValue());
183
184                                        dialog.setVisible(false);
185                                }
186                                selected = list.getSelectedIndex();
187                        }
188
189                        @Override
190                        public void mouseExited(MouseEvent e) {
191                        }
192
193                        @Override
194                        public void mouseEntered(MouseEvent e) {
195                        }
196
197                        @Override
198                        public void mouseClicked(MouseEvent e) {
199                        }
200                });
201                dialog.add(new JScrollPane(list, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
202                                JScrollPane.HORIZONTAL_SCROLLBAR_NEVER));
203                dialog.pack();
204                addKeyListener(new KeyListener() {
205                        @Override
206                        public void keyTyped(KeyEvent e) {
207                        }
208
209                        @Override
210                        public void keyPressed(KeyEvent e) {
211                                updateLocation();
212                        }
213
214                        @Override
215                        public void keyReleased(KeyEvent e) {
216                                if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
217                                        dialog.setVisible(false);
218                                        return;
219                                } else if (e.getKeyCode() == KeyEvent.VK_DOWN) {
220                                        if (dialog.isVisible()) {
221                                                list.setSelectedIndex(list.getSelectedIndex() + 1);
222                                                list.ensureIndexIsVisible(list.getSelectedIndex() + 1);
223                                                return;
224                                        } else {
225                                                showSuggest();
226                                        }
227                                } else if (e.getKeyCode() == KeyEvent.VK_UP) {
228                                        list.setSelectedIndex(list.getSelectedIndex() - 1);
229                                        list.ensureIndexIsVisible(list.getSelectedIndex() - 1);
230                                        return;
231                                } else if (e.getKeyCode() == KeyEvent.VK_ENTER
232                                                && list.getSelectedIndex() != -1 && suggestions.size() > 0) {
233                                        setText((String) list.getSelectedValue());
234
235
236                                        dialog.setVisible(false);
237                                        return;
238                                }
239                                showSuggest();
240                        }
241                });
242
243        }
244
245        private void init(){
246                autoSuggestProvider =  new DefaultAutoSuggestProvider();
247                lastWord = "";
248                regular = getFont();
249                busy = new Font(getFont().getName(), Font.ITALIC, getFont().getSize());
250                suggestions = new Vector<String>();
251                defaultText = DEFAULT_TEXT;
252
253
254                dialog = new JDialog();
255                dialog.setUndecorated(true);
256                dialog.setFocusableWindowState(false);
257                dialog.setFocusable(false);
258
259                initSuggestionList();
260        }
261
262        public String getDefaultText() {
263                return defaultText;
264        }
265
266        public void setDefaultText(String defaultText) {
267                this.defaultText = defaultText;
268        }
269
270        public AutoSuggestProvider getAutoSuggestProvider() {
271                return autoSuggestProvider;
272        }
273
274        public void setAutoSuggestProvider(AutoSuggestProvider autoSuggestProvider) {
275                this.autoSuggestProvider = autoSuggestProvider;
276        }
277
278        /**
279         * Force the suggestions to be displayed (Useful for buttons
280         * e.g. for using JSuggestionField like a ComboBox)
281         */
282        public void showSuggest() {
283
284                assert(getText() != null);
285                lastWord = getText().trim();
286                        //autoSuggestProvider.getSuggestion(lastWord);
287
288
289
290                if (!getText().toLowerCase().contains(lastWord.toLowerCase())) {
291                        suggestions.clear();
292                }
293
294                if (matcher != null) {
295                        matcher.setStop();
296                }
297                matcher = new SuggestionFetcher();
298
299                //SwingUtilities.invokeLater(matcher);
300                matcher.execute();
301                lastWord = getText().trim();
302                updateLocation();
303        }
304
305        /**
306         * Force the suggestions to be hidden (Useful for buttons, e.g. to use
307         * JSuggestionField like a ComboBox)
308         */
309        public void hideSuggest() {
310                dialog.setVisible(false);
311        }
312
313        /**
314         * @return boolean Visibility of the suggestion window
315         */
316        public boolean isSuggestVisible() {
317                return dialog.isVisible();
318        }
319
320
321
322        /**
323         * Place the suggestion window under the JTextField.
324         */
325        private void updateLocation() {
326                try {
327                        location = getLocationOnScreen();
328                        location.y += getHeight();
329                        dialog.setLocation(location);
330                } catch (IllegalComponentStateException e) {
331                        return; // might happen on window creation
332                }
333        }
334
335
336        /** fetch suggestions from SuggestionProvider
337         *
338         *
339         *
340         */
341        private class SuggestionFetcher extends SwingWorker<String, Object> {
342                /** flag used to stop the thread */
343                private AtomicBoolean stop = new AtomicBoolean(false);
344
345                String previousWord;
346                /**
347                 * Standard run method used in threads
348                 * responsible for the actual search
349                 */
350                @Override
351                public String doInBackground() {
352                        try {
353                                setFont(busy);
354                                String userInput = getText().trim();
355                                if ( userInput == null || userInput.equals(""))
356                                        return "";
357
358                                if ( previousWord != null){
359                                        if ( userInput.equals(previousWord))
360                                                return "";
361                                }
362                                previousWord = userInput;
363
364                                suggestions = autoSuggestProvider.getSuggestion(userInput);
365
366                                setFont(regular);
367
368                                if (suggestions.size() > 0) {
369                                        list.setListData(suggestions);
370                                        list.setSelectedIndex(0);
371                                        list.ensureIndexIsVisible(0);
372                                        dialog.setVisible(true);
373                                } else {
374                                        dialog.setVisible(false);
375                                }
376                        } catch (Exception e) {
377                                //e.printStackTrace();
378                                // ignore...
379
380                        }
381                        return "Done.";
382                }
383
384                public void setStop(){
385                        stop.set(true);
386                }
387
388
389        }
390}