bcfdaaa48ae7b70f6e44c1793bfe6b353ef9e930
[scilab.git] / scilab / modules / completion / src / java / org / scilab / modules / completion / AbstractSciCompletionWindow.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2007-2008 - INRIA - Vincent COUVERT
4  * Copyright (C) 2010-2011 - Calixte DENIZET
5  *
6  * Copyright (C) 2012 - 2016 - Scilab Enterprises
7  *
8  * This file is hereby licensed under the terms of the GNU GPL v2.0,
9  * pursuant to article 5.3.4 of the CeCILL v.2.1.
10  * This file was originally licensed under the terms of the CeCILL v2.1,
11  * and continues to be available under such terms.
12  * For more information, see the COPYING file which you should have received
13  * along with this program.
14  *
15  */
16
17 package org.scilab.modules.completion;
18
19 import java.awt.BorderLayout;
20 import java.awt.Component;
21 import java.awt.Cursor;
22 import java.awt.Dimension;
23 import java.awt.Point;
24 import java.awt.event.FocusEvent;
25 import java.awt.event.FocusListener;
26 import java.awt.event.KeyEvent;
27 import java.awt.event.KeyListener;
28 import java.awt.event.MouseEvent;
29 import java.awt.event.MouseListener;
30 import java.awt.event.MouseMotionListener;
31 import java.util.ArrayList;
32 import java.util.Collections;
33 import java.util.List;
34
35 import javax.swing.AbstractListModel;
36 import javax.swing.JComponent;
37 import javax.swing.text.JTextComponent;
38 import javax.swing.JLabel;
39 import javax.swing.JList;
40 import javax.swing.JPanel;
41 import javax.swing.JScrollPane;
42
43 import com.artenum.rosetta.ui.CompletionItemListCellRenderer;
44 import com.artenum.rosetta.interfaces.core.CompletionItem;
45 import com.artenum.rosetta.interfaces.core.InputParsingManager;
46 import com.artenum.rosetta.interfaces.ui.CompletionWindow;
47
48 import org.scilab.modules.completion.Completion;
49 import org.scilab.modules.localization.Messages;
50
51 /**
52  * Scilab completion window main class
53  * @author Vincent COUVERT
54  * @author Calixte DENIZET
55  */
56 public abstract class AbstractSciCompletionWindow implements CompletionWindow, KeyListener, FocusListener, MouseMotionListener, MouseListener {
57
58     private static final int WINDOW_WIDTH = 300;
59     private static final int WINDOW_HEIGHT = 100;
60
61     protected JPanel window;
62     protected CompletionItemListModel model;
63     protected JList listUI;
64     protected JScrollPane scrollPane;
65     protected InputParsingManager inputParsingManager;
66     protected JComponent focusOutComponent;
67
68     protected int currentCaretPosition;
69
70     /**
71      * Default constructor
72      */
73     public AbstractSciCompletionWindow() {
74     }
75
76     /**
77      * @return the JTextComponent where the CompletionWindow will be drawn
78      */
79     public abstract JTextComponent getTextComponent();
80
81     /**
82      * @return the list of the possibilities to complete
83      */
84     public abstract List<CompletionItem> getCompletionItems();
85
86     /**
87      * Set the component that should take the focus when the completion window is hidden
88      * @param component the component to focus
89      * @see com.artenum.rosetta.interfaces.ui.CompletionWindow#setFocusOut(javax.swing.JComponent)
90      */
91     public void setFocusOut(JComponent component) {
92         focusOutComponent = component;
93     }
94
95     /**
96      * Set the input parsing manager associated to this completion window
97      * @param inputParsingManager the input parsing manager to set
98      * @see com.artenum.rosetta.interfaces.ui.CompletionWindow#setInputParsingManager(com.artenum.rosetta.interfaces.core.InputParsingManager)
99      */
100     public void setInputParsingManager(InputParsingManager inputParsingManager) {
101         this.inputParsingManager = inputParsingManager;
102     }
103
104     /**
105      * Set the parent component for the completion window
106      * @param component the parent component
107      * Caution, the component shouldn't be null otherwise the completion window
108      * will never get the focus
109      */
110     public void setGraphicalContext(Component component) {
111
112         /* List to display all completion items */
113         model = new CompletionItemListModel();
114         listUI = new JList(model);
115         listUI.setCellRenderer(new CompletionItemListCellRenderer());
116         scrollPane = new JScrollPane(listUI, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
117
118         /* Utility to able the user to resize the window */
119         JLabel windowResizeCorner = new JLabel("~", JLabel.CENTER);
120         windowResizeCorner.addMouseMotionListener(this);
121         scrollPane.setCorner(JScrollPane.LOWER_RIGHT_CORNER, windowResizeCorner);
122
123         /* Completion window */
124         window = new JPanel(new BorderLayout());
125         window.add(scrollPane, BorderLayout.CENTER);
126         window.setSize(new Dimension(WINDOW_WIDTH, WINDOW_HEIGHT));
127         window.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
128
129         /* Overide Listener */
130         listUI.getInputMap().clear();
131         scrollPane.getInputMap().clear();
132         /* Item selection is available using the keyboard */
133         listUI.addKeyListener(this);
134         /* Item selection is available using the mouse */
135         listUI.addFocusListener(this);
136         listUI.addMouseListener(this);
137
138         getTextComponent().add(window);
139
140         setVisible(false);
141     }
142
143     /**
144      * Make the completion window visible and update its content
145      * @param list list of items matching completion
146      * @param location position of the top left corner of the window
147      * @see com.artenum.rosetta.interfaces.ui.CompletionWindow#show(java.util.List, java.awt.Point)
148      */
149     public abstract void show(List<CompletionItem> list, Point location);
150
151     /**
152      * Get the character string selected by the user as a completion
153      * @return the character string
154      * @see com.artenum.rosetta.interfaces.ui.CompletionWindow#getCompletionResult()
155      */
156     public String getCompletionResult() {
157         return ((CompletionItem) listUI.getSelectedValue()).getReturnValue();
158     }
159
160     /**
161      * Get type of the character string selected by the user as a completion
162      * @return the character string
163      */
164
165     public String getCompletionResultType() {
166         return ((CompletionItem) listUI.getSelectedValue()).getType();
167     }
168
169     /**
170      * Get the visibility status of the completion window
171      * @return true if the window is visible (false else)
172      */
173     public boolean isVisible() {
174         return window.isVisible();
175     }
176
177     /**
178      * Set the visibility status of the completion window
179      * @param status true if the window is visible (false else)
180      */
181     public void setVisible(boolean status) {
182         window.setVisible(status);
183         if (!status) {
184             focusOutComponent.grabFocus();
185         }
186     }
187     /**
188      * List model which allow filter on completion item
189      * In Scilab, filter and sort are done by Scilab and have not to be done here
190      */
191     protected static class CompletionItemListModel extends AbstractListModel {
192         private static final long serialVersionUID = 1L;
193         private List<CompletionItem> data;
194         private String filter;
195
196         /**
197          * Default constructor
198          */
199         public CompletionItemListModel() {
200             data = new ArrayList<CompletionItem>();
201         }
202
203         /**
204          * Get element in the data list
205          * @param index the index of the element to get
206          * @return the corresponding element
207          * @see javax.swing.ListModel#getElementAt(int)
208          */
209         public Object getElementAt(int index) {
210             /* Filtering is done by Scilab */
211             return data.get(index);
212         }
213
214         /**
215          * Get the number of entries in the list
216          * @return the size
217          * @see javax.swing.ListModel#getSize()
218          */
219         public int getSize() {
220             /* Filtering is done by Scilab */
221             return data.size();
222         }
223
224         /**
225          * Set the filter to apply to the dictionnary
226          * @param filterToSet the filter to set
227          */
228         public void setFilter(String filterToSet) {
229
230             if ((filterToSet == null) || ((filterToSet != null) && (filterToSet.length() == 0))) {
231                 filter = null;
232             } else {
233                 filter = filterToSet;
234             }
235             fireContentsChanged(this, 0, getSize());
236         }
237
238         /**
239          * Update the list items
240          * @param list list of item to set to the list
241          * @param list
242          */
243         public void updateData(List<CompletionItem> list) {
244             data.clear();
245             data.addAll(list);
246             Collections.sort(data);
247             setFilter(null);
248         }
249     }
250
251     /**
252      * Add the completed word in the TextComponent
253      * @param position where to append the completed word
254      */
255     public void addCompletedWord(int position) {
256         String currentLine = inputParsingManager.getCommandLine();
257         String lineBeforeCaret = currentLine.substring(0, position);
258         String lineAfterCaret = currentLine.substring(position);
259
260         String stringToAdd = getCompletionResult();
261         String stringToAddType = getCompletionResultType();
262
263         boolean typeStringIsFile = false;
264
265         if (stringToAddType.equals(Messages.gettext("File or Directory"))) {
266             typeStringIsFile = true;
267         }
268
269         String newLine = Completion.completelineforjava(lineBeforeCaret, stringToAdd, typeStringIsFile, lineAfterCaret);
270
271         inputParsingManager.reset();
272         inputParsingManager.append(newLine);
273     }
274
275     /**
276      * Add the completed word in the TextComponent
277      * @param stringToAdd the string to add
278      * @param position where to append the completed word
279      */
280     public void addCompletedWord(String stringToAdd, int position) {
281         String currentLine = inputParsingManager.getCommandLine();
282         String lineBeforeCaret = currentLine.substring(0, position);
283         String lineAfterCaret = currentLine.substring(position);
284
285         String newLine = Completion.completelineforjava(lineBeforeCaret, stringToAdd, false, lineAfterCaret);
286
287         inputParsingManager.reset();
288         inputParsingManager.append(newLine);
289     }
290
291     /**
292      * Management of the key typing for the filtering
293      * @param e event
294      */
295     public void keyPressed(KeyEvent e) {
296         if (e.getKeyCode() == KeyEvent.VK_ENTER) {
297             /* The user validates an entry in the list */
298
299             /* Add text to the input command view */
300             if (listUI.getModel().getSize() != 0) {
301                 addCompletedWord(currentCaretPosition);
302             }
303
304             /* Hide the completion window and give the focus to the console */
305             setVisible(false);
306             focusOutComponent.grabFocus();
307         } else if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
308             /* The user want to exit from completion mode */
309
310             /* Hide the completion window and give the focus to the console */
311             setVisible(false);
312             focusOutComponent.grabFocus();
313         } else if (e.getKeyCode() == KeyEvent.VK_DOWN) {
314             /* The user want to select next item */
315
316             if (model.getSize() > 0) {
317                 listUI.setSelectedIndex((listUI.getSelectedIndex()) % model.getSize());
318             }
319         } else if (e.getKeyCode() == KeyEvent.VK_UP) {
320             /* The user want to select previous item */
321
322             if (model.getSize() > 0) {
323                 listUI.setSelectedIndex((model.getSize() + listUI.getSelectedIndex()) % model.getSize());
324             }
325         } else if (e.getKeyCode() == KeyEvent.VK_BACK_SPACE) {
326             /* Delete a character in Scilab input command view */
327             if (inputParsingManager.getPartLevel(inputParsingManager.getCompletionLevel()) != null) {
328                 // Remove a key in input command line
329                 inputParsingManager.backspace();
330
331                 // Display new completion list
332                 Point location = inputParsingManager.getWindowCompletionLocation();
333                 this.show(getCompletionItems(), location);
334                 listUI.setSelectedIndex(0);
335             } else {
336                 /* Hide the completion window and give the focus to the console */
337                 setVisible(false);
338                 focusOutComponent.grabFocus();
339             }
340         } else if (e.getKeyCode() != KeyEvent.VK_TAB) { // IGNORE TAB KEYS BECAUSE ALREADY IN COMPLETION MODE
341             // Add a character in Scilab input command view
342             if (e.getKeyChar() != KeyEvent.CHAR_UNDEFINED) {
343                 // Add a key in input command line
344                 inputParsingManager.append(String.valueOf(e.getKeyChar()));
345
346                 // Display new completion list
347                 Point location = inputParsingManager.getWindowCompletionLocation();
348                 this.show(getCompletionItems(), location);
349                 listUI.setSelectedIndex(0);
350             }
351         }
352     }
353
354     /**
355      * What to do when a key is released ?
356      * @param e event
357      */
358     public void keyReleased(KeyEvent e) {
359     }
360
361     /**
362      * What to do when a key is typed ?
363      * @param e event
364      */
365     public void keyTyped(KeyEvent e) {
366     }
367
368     /**
369      * What to do when the completion window gets the focus ?
370      * @param e event
371      */
372     public void focusGained(FocusEvent e) {
373     }
374
375     /**
376      * To support the auto hide when focus is lost
377      * @param e event
378      */
379     public void focusLost(FocusEvent e) {
380         setVisible(false);
381     }
382
383     /**
384      * To support the completion window resize
385      * @param e event
386      */
387     public void mouseDragged(MouseEvent e) {
388         Point origine = window.getLocationOnScreen();
389         Point destination = ((Component) e.getSource()).getLocationOnScreen();
390         destination.x += e.getX();
391         destination.y += e.getY();
392         destination.translate(-origine.x, -origine.y);
393         window.setSize(destination.x, destination.y);
394     }
395
396     /**
397      * What to do when the mouse is moved over the completion window ?
398      * @param e event
399      */
400     public void mouseMoved(MouseEvent e) {
401     }
402
403     /**
404      * What to do when a mouse button is clicked over the completion window ?
405      * @param e event
406      */
407     public void mouseClicked(MouseEvent e) {
408         if (model.getSize() > 0) {
409             /* Select the list item under the mouse */
410             listUI.setSelectedIndex((listUI.getSelectedIndex()) % model.getSize());
411
412             if (e.getClickCount() >= 2) { /* Double click = the user validates the item */
413                 addCompletedWord(currentCaretPosition);
414
415                 /* Hide the completion window and give the focus to the console */
416                 setVisible(false);
417                 focusOutComponent.grabFocus();
418             }
419         }
420     }
421
422     /**
423      * What to do when the mouse enters the completion window ?
424      * @param e event
425      */
426     public void mouseEntered(MouseEvent e) {
427     }
428
429     /**
430      * What to do when the mouse exits over the completion window ?
431      * @param e event
432      */
433     public void mouseExited(MouseEvent e) {
434     }
435
436     /**
437      * What to do when a mouse button mouse is pressed over the completion window ?
438      * @param e event
439      */
440     public void mousePressed(MouseEvent e) {
441     }
442
443     /**
444      * What to do when a mouse button mouse is released over the completion window ?
445      * @param e event
446      */
447     public void mouseReleased(MouseEvent e) {
448         window.validate();
449     }
450 }