184ae880142110119edf363f61a539eee07ac541
[scilab.git] / scilab / modules / gui / src / java / org / scilab / modules / gui / bridge / editbox / SwingScilabEditBox.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) 2007 - INRIA - Marouane BEN JELLOUL
5  * Copyright (C) 2011 - DIGITEO - Vincent COUVERT
6  *
7  * This file must be used under the terms of the CeCILL.
8  * This source file is licensed as described in the file COPYING, which
9  * you should have received as part of this distribution.  The terms
10  * are also available at
11  * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt
12  *
13  */
14
15 package org.scilab.modules.gui.bridge.editbox;
16
17 import static org.scilab.modules.graphic_objects.graphicObject.GraphicObjectProperties.__GO_UI_SCROLLABLE__;
18 import static org.scilab.modules.graphic_objects.graphicObject.GraphicObjectProperties.__GO_UI_MAX__;
19 import static org.scilab.modules.graphic_objects.graphicObject.GraphicObjectProperties.__GO_UI_BACKGROUNDCOLOR__;
20 import static org.scilab.modules.graphic_objects.graphicObject.GraphicObjectProperties.__GO_UI_MIN__;
21 import static org.scilab.modules.graphic_objects.graphicObject.GraphicObjectProperties.__GO_UI_STRING__;
22
23 import java.awt.BorderLayout;
24 import java.awt.Color;
25 import java.awt.Font;
26 import java.awt.KeyboardFocusManager;
27 import java.awt.event.ActionEvent;
28 import java.awt.event.FocusEvent;
29 import java.awt.event.FocusListener;
30 import java.awt.event.KeyEvent;
31
32 import javax.swing.AbstractAction;
33 import javax.swing.InputMap;
34 import javax.swing.JPanel;
35 import javax.swing.JScrollPane;
36 import javax.swing.JTextPane;
37 import javax.swing.KeyStroke;
38 import javax.swing.ScrollPaneConstants;
39 import javax.swing.UIManager;
40 import javax.swing.border.Border;
41 import javax.swing.text.AbstractDocument;
42 import javax.swing.text.BoxView;
43 import javax.swing.text.ComponentView;
44 import javax.swing.text.Element;
45 import javax.swing.text.IconView;
46 import javax.swing.text.LabelView;
47 import javax.swing.text.ParagraphView;
48 import javax.swing.text.SimpleAttributeSet;
49 import javax.swing.text.StyleConstants;
50 import javax.swing.text.StyledDocument;
51 import javax.swing.text.StyledEditorKit;
52 import javax.swing.text.View;
53 import javax.swing.text.ViewFactory;
54
55 import org.scilab.modules.graphic_objects.console.Console;
56 import org.scilab.modules.graphic_objects.graphicController.GraphicController;
57 import org.scilab.modules.gui.SwingView;
58 import org.scilab.modules.gui.SwingViewObject;
59 import org.scilab.modules.gui.SwingViewWidget;
60 import org.scilab.modules.gui.editbox.SimpleEditBox;
61 import org.scilab.modules.gui.events.callback.CommonCallBack;
62 import org.scilab.modules.gui.menubar.MenuBar;
63 import org.scilab.modules.gui.textbox.TextBox;
64 import org.scilab.modules.gui.toolbar.ToolBar;
65 import org.scilab.modules.gui.utils.Position;
66 import org.scilab.modules.gui.utils.PositionConverter;
67 import org.scilab.modules.gui.utils.ScilabRelief;
68 import org.scilab.modules.gui.utils.ScilabSwingUtilities;
69 import org.scilab.modules.gui.utils.Size;
70
71 /**
72  * Swing implementation for Scilab EditBox in GUIs
73  * @author Vincent COUVERT
74  */
75 public class SwingScilabEditBox extends JScrollPane implements SwingViewObject, SimpleEditBox {
76
77     private static final long serialVersionUID = 2048261239598753717L;
78
79     private Integer uid;
80
81     private CommonCallBack callback;
82
83     private FocusListener focusListener;
84
85     private Border defaultBorder = null;
86
87     private StyledDocument doc;
88     private SimpleAttributeSet docAttributes = new SimpleAttributeSet();
89
90     private JTextPane textPane = new JTextPane();
91     //use to disable wordwarp
92     private JPanel noWrapPanel = new JPanel(new BorderLayout());
93
94     private Object enterKeyAction;
95     private Object tabKeyAction;
96     private Object shiftTabKeyAction;
97
98     private class EditBoxView extends BoxView {
99         public EditBoxView(Element elem, int axis) {
100             super(elem, axis);
101         }
102
103         protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
104             super.layoutMajorAxis(targetSpan, axis, offsets, spans);
105             int textBlockHeight = 0;
106             int offset = 0;
107
108             if (textPane.getAlignmentY() == BOTTOM_ALIGNMENT) {
109                 for (int i = 0; i < spans.length; i++) {
110                     textBlockHeight += spans[i];
111                 }
112                 offset = (targetSpan - textBlockHeight);
113                 for (int i = 0; i < offsets.length; i++) {
114                     offsets[i] += offset;
115                 }
116             } else if (textPane.getAlignmentY() == CENTER_ALIGNMENT) {
117                 for (int i = 0; i < spans.length; i++) {
118                     textBlockHeight += spans[i];
119                 }
120                 offset = (targetSpan - textBlockHeight) / 2;
121                 for (int i = 0; i < offsets.length; i++) {
122                     offsets[i] += offset;
123                 }
124             } else {
125                 // TOP_ALIGNEMENT or other
126                 // default behaviour : do nothing special
127             }
128         }
129     }
130
131     private class EditBoxEditorKit extends StyledEditorKit {
132         private static final long serialVersionUID = -3293325523458217074L;
133
134         public ViewFactory getViewFactory() {
135             return new ViewFactory() {
136                 public View create(Element elem) {
137                     String kind = elem.getName();
138                     if (kind != null) {
139                         if (kind.equals(AbstractDocument.ContentElementName)) {
140                             return new LabelView(elem);
141                         } else if (kind.equals(AbstractDocument.ParagraphElementName)) {
142                             return new ParagraphView(elem);
143                         } else if (kind.equals(AbstractDocument.SectionElementName)) {
144                             return new EditBoxView(elem, View.Y_AXIS);
145                         } else if (kind.equals(StyleConstants.ComponentElementName)) {
146                             return new ComponentView(elem);
147                         } else if (kind.equals(StyleConstants.IconElementName)) {
148                             return new IconView(elem);
149                         }
150                     }
151                     return new LabelView(elem);
152                 }
153             };
154         }
155     }
156
157     /**
158      * Constructor
159      */
160     public SwingScilabEditBox() {
161         super(new JTextPane());
162         textPane = (JTextPane) getViewport().getView();
163         setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
164
165         textPane.setEditorKit(new EditBoxEditorKit());
166         doc = (StyledDocument) textPane.getDocument();
167
168         // Create a focus listener to call the callback action
169         focusListener = new FocusListener() {
170             public void focusGained(FocusEvent arg0) {
171                 // Do nothing
172             }
173
174             public void focusLost(FocusEvent arg0) {
175                 validateUserInput();
176             }
177         };
178         textPane.addFocusListener(focusListener);
179         KeyStroke enterKey = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
180         KeyStroke tabKey = KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0);
181         KeyStroke shiftTabKey = KeyStroke.getKeyStroke(KeyEvent.VK_TAB, KeyEvent.SHIFT_DOWN_MASK);
182         InputMap map = textPane.getInputMap();
183         enterKeyAction = map.get(enterKey);
184         tabKeyAction = map.get(tabKey);
185         shiftTabKeyAction = map.get(shiftTabKey);
186
187         if (Console.getConsole().getUseDeprecatedLF() == false) {
188             setEditFont(getFont());
189         }
190     }
191
192     /**
193      * Validate UserInput and call Scilab Callback if needed
194      */
195     private void validateUserInput() {
196         // Validates user input
197         if (getParent() != null) { // To avoid to execute the callback when then parent figure is destroyed
198
199             String[] stringProperty = getText().split("\n");
200             GraphicController.getController().setProperty(uid, __GO_UI_STRING__, stringProperty);
201
202             if (SwingView.getFromId(uid) != null && callback != null) {
203                 callback.actionPerformed(null);
204             }
205         }
206     }
207
208     /**
209      * Draws a swing Scilab EditBox
210      * @see org.scilab.modules.gui.uielement.UIElement#draw()
211      */
212     public void draw() {
213         this.setVisible(true);
214         this.doLayout();
215     }
216
217     /**
218      * Gets the dimensions (width and height) of a swing Scilab EditBox
219      * @return the dimensions of the EditBox
220      * @see org.scilab.modules.gui.uielement.UIElement#getDims()
221      */
222     public Size getDims() {
223         return new Size(this.getSize().width, this.getSize().height);
224     }
225
226     /**
227      * Gets the position (X-coordinate and Y-coordinate) of a swing Scilab
228      * EditBox
229      * @return the position of the EditBox
230      * @see org.scilab.modules.gui.uielement.UIElement#getPosition()
231      */
232     public Position getPosition() {
233         return PositionConverter.javaToScilab(getLocation(), getSize(), getParent());
234     }
235
236     /**
237      * Sets the dimensions (width and height) of a swing Scilab EditBox
238      * @param newSize the dimensions we want to set to the EditBox
239      * @see org.scilab.modules.gui.uielement.UIElement#setDims(org.scilab.modules.gui.utils.Size)
240      */
241     public void setDims(Size newSize) {
242         this.setSize(newSize.getWidth(), newSize.getHeight());
243     }
244
245     /**
246      * Sets the position (X-coordinate and Y-coordinate) of a swing Scilab
247      * EditBox
248      * @param newPosition the position we want to set to the EditBox
249      * @see org.scilab.modules.gui.uielement.UIElement#setPosition(org.scilab.modules.gui.utils.Position)
250      */
251     public void setPosition(Position newPosition) {
252         Position javaPosition = PositionConverter.scilabToJava(newPosition, getDims(), getParent());
253         setLocation(javaPosition.getX(), javaPosition.getY());
254     }
255
256     /**
257      * Add a callback to the EditBox
258      * @param cb the callback to set.
259      */
260     public void setCallback(CommonCallBack cb) {
261         this.callback = cb;
262     }
263
264     public void setText(String[] texts) {
265         StringBuffer newText = new StringBuffer(texts[0]);
266
267         for (int i = 1; i < texts.length; ++i) {
268             newText.append("\n" + texts[i]);
269         }
270
271         try {
272             textPane.setText(newText.toString());
273             doc.setParagraphAttributes(0, doc.getLength() - 1, docAttributes, true);
274         } catch (Exception e) {
275             // Do nothing
276         }
277     }
278
279     public void setText(String text) {
280         try {
281             textPane.setText(text);
282             doc.setParagraphAttributes(0, doc.getLength() - 1, docAttributes, true);
283         } catch (Exception e) {
284             // Do nothing
285         }
286     }
287
288     /**
289      * Set if the EditBox is enabled or not
290      * @param status true if the EditBox is enabled
291      */
292     public void setEnabled(boolean status) {
293         super.setEnabled(status);
294         textPane.setEnabled(status);
295
296         //force background to gray
297         if (status) {
298             SwingViewWidget.update(this, __GO_UI_BACKGROUNDCOLOR__, GraphicController.getController().getProperty(uid, __GO_UI_BACKGROUNDCOLOR__));
299         } else {
300             Color gray = new Color(0.9f,  0.9f,  0.9f);
301             setBackground(gray);
302         }
303
304
305         /* (Des)Activate the callback */
306         if (callback != null) {
307             if (status) {
308                 removeFocusListener(focusListener); /*
309                                                      * To be sure the callback
310                                                      * is not added two times
311                                                      */
312                 //removeActionListener(actionListener); /* To be sure the callback is not added two times */
313                 addFocusListener(focusListener);
314                 //addActionListener(actionListener);
315             } else {
316                 removeFocusListener(focusListener);
317                 //removeActionListener(actionListener);
318             }
319         }
320     }
321
322     /**
323      * Setter for MenuBar
324      * @param menuBarToAdd the MenuBar associated to the Tab.
325      */
326     public void addMenuBar(MenuBar menuBarToAdd) {
327         /* Unimplemented for EditBoxes */
328         throw new UnsupportedOperationException();
329     }
330
331     /**
332      * Setter for ToolBar
333      * @param toolBarToAdd the ToolBar associated to the Tab.
334      */
335     public void addToolBar(ToolBar toolBarToAdd) {
336         /* Unimplemented for EditBoxes */
337         throw new UnsupportedOperationException();
338     }
339
340     /**
341      * Getter for MenuBar
342      * @return MenuBar: the MenuBar associated to the Tab.
343      */
344     public MenuBar getMenuBar() {
345         /* Unimplemented for EditBoxes */
346         throw new UnsupportedOperationException();
347     }
348
349     /**
350      * Getter for ToolBar
351      * @return ToolBar: the ToolBar associated to the Tab.
352      */
353     public ToolBar getToolBar() {
354         /* Unimplemented for EditBoxes */
355         throw new UnsupportedOperationException();
356     }
357
358     /**
359      * Set the horizontal alignment for the EditBox text
360      * @param alignment the value for the alignment (See ScilabAlignment.java)
361      */
362     public void setHorizontalAlignment(String alignment) {
363         if (alignment.equals("") == false) {
364             int alignConstant = StyleConstants.ALIGN_LEFT;
365             if (alignment.equals("right")) {
366                 alignConstant = StyleConstants.ALIGN_RIGHT;
367             } else if (alignment.equals("center")) {
368                 alignConstant = StyleConstants.ALIGN_CENTER;
369             }
370
371             StyleConstants.setAlignment(docAttributes, alignConstant);
372             doc.setParagraphAttributes(0, doc.getLength(), docAttributes, true);
373         }
374     }
375
376     /**
377      * Set the vertical alignment for the EditBox text
378      * @param alignment the value for the alignment (See ScilabAlignment.java)
379      */
380     public void setVerticalAlignment(String alignment) {
381         if (alignment.equals("") == false) {
382             if (alignment.equals("bottom")) {
383                 textPane.setAlignmentY(BOTTOM_ALIGNMENT);
384             } else if (alignment.equals("top")) {
385                 textPane.setAlignmentY(TOP_ALIGNMENT);
386             } else if (alignment.equals("middle")) {
387                 textPane.setAlignmentY(CENTER_ALIGNMENT);
388             }
389             // Force text update to render
390             setText(getText());
391         }
392     }
393
394     /**
395      * Set the Relief of the EditBox
396      * @param reliefType the type of the relief to set (See ScilabRelief.java)
397      */
398     public void setRelief(String reliefType) {
399         if (defaultBorder == null) {
400             defaultBorder = textPane.getBorder();
401         }
402         textPane.setBorder(ScilabRelief.getBorderFromRelief(reliefType, defaultBorder));
403     }
404
405     /**
406      * Destroy the EditBox
407      */
408     public void destroy() {
409         ScilabSwingUtilities.removeFromParent(this);
410     }
411
412     /**
413      * Setter for InfoBar
414      * @param infoBarToAdd the InfoBar associated to the EditBox.
415      */
416     public void addInfoBar(TextBox infoBarToAdd) {
417         /* Unimplemented for EditBoxes */
418         throw new UnsupportedOperationException();
419     }
420
421     /**
422      * Getter for InfoBar
423      * @return the InfoBar associated to the EditBox.
424      */
425     public TextBox getInfoBar() {
426         /* Unimplemented for EditBoxes */
427         throw new UnsupportedOperationException();
428     }
429
430     /**
431      * Set the UID
432      * @param id the UID
433      */
434     public void setId(Integer id) {
435         uid = id;
436     }
437
438     /**
439      * Get the UID
440      * @return the UID
441      */
442     public Integer getId() {
443         return uid;
444     }
445
446     public void setBackground(Color bg) {
447         super.setBackground(bg);
448         if (docAttributes != null && textPane != null) {
449             textPane.setBackground(bg);
450             StyleConstants.setBackground(docAttributes, bg);
451         }
452     }
453
454     public void setEditFont(Font font) {
455         super.setFont(font);
456         if (textPane != null) {
457             textPane.setFont(font);
458             StyleConstants.setFontFamily(docAttributes, font.getFamily());
459             StyleConstants.setFontSize(docAttributes, font.getSize());
460             StyleConstants.setBold(docAttributes, font.isBold());
461             StyleConstants.setItalic(docAttributes, font.isItalic());
462         }
463     }
464
465     public void setFont(Font font) {
466         setEditFont(font);
467     }
468
469     /**
470      * Generic update method
471      * @param property property name
472      * @param value property value
473      */
474     public void update(int property, Object value) {
475         GraphicController controller = GraphicController.getController();
476
477         switch (property) {
478             case __GO_UI_MAX__: {
479                 double min = ((Double) controller.getProperty(uid, __GO_UI_MIN__));
480                 double max = ((Double) controller.getProperty(uid, __GO_UI_MAX__));
481                 if (max - min > 1.0) {
482                     setMultiLineText(true);
483                 } else {
484                     setMultiLineText(false);
485                 }
486                 // Force String update
487                 update(__GO_UI_STRING__, GraphicController.getController().getProperty(uid, __GO_UI_STRING__));
488                 break;
489             }
490             case __GO_UI_MIN__: {
491                 Double min = ((Double) value);
492                 Double max = ((Double) controller.getProperty(uid, __GO_UI_MAX__));
493                 if (max - min > 1.0) {
494                     setMultiLineText(true);
495                 } else {
496                     setMultiLineText(false);
497                 }
498                 // Force String update
499                 update(__GO_UI_STRING__, GraphicController.getController().getProperty(uid, __GO_UI_STRING__));
500                 break;
501             }
502             case __GO_UI_STRING__: {
503                 double min = ((Double) controller.getProperty(uid, __GO_UI_MIN__));
504                 double max = ((Double) controller.getProperty(uid, __GO_UI_MAX__));
505                 if (max - min > 1.0) {
506                     setText((String[]) value);
507                     setMultiLineText(true);
508                 } else {
509                     setText(((String[]) value)[0]);
510                     setMultiLineText(false);
511                 }
512                 break;
513             }
514             case __GO_UI_SCROLLABLE__ : {
515                 setScrollable((Boolean)value);
516                 break;
517             }
518             default: {
519                 SwingViewWidget.update(this, property, value);
520                 break;
521             }
522         }
523     }
524
525     public void setScrollable(Boolean scrollable) {
526         if (scrollable) {
527             setViewportView(noWrapPanel);
528             noWrapPanel.add(textPane);
529         } else {
530             setViewportView(textPane);
531         }
532     }
533
534     public String getText() {
535         return textPane.getText();
536     }
537
538     public void setMultiLineText(boolean enable) {
539         KeyStroke enterKey = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
540         KeyStroke tabKey = KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0);
541         KeyStroke shiftTabKey = KeyStroke.getKeyStroke(KeyEvent.VK_TAB, KeyEvent.SHIFT_DOWN_MASK);
542         if (enable) {
543             setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
544             textPane.getInputMap().remove(enterKey);
545             textPane.getInputMap().remove(tabKey);
546             textPane.getInputMap().remove(shiftTabKey);
547             textPane.getInputMap().put(enterKey, enterKeyAction);
548             textPane.getInputMap().put(tabKey, tabKeyAction);
549             textPane.getInputMap().put(tabKey, shiftTabKeyAction);
550         } else {
551             setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
552             AbstractAction focusNextComponent = new AbstractAction() {
553                 private static final long serialVersionUID = -5286137769378297783L;
554
555                 public void actionPerformed(ActionEvent e) {
556                     KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
557                     manager.focusNextComponent();
558                 }
559             };
560             
561             AbstractAction validateUserInput = new AbstractAction() {
562                 private static final long serialVersionUID = -5286137769378297783L;
563
564                 public void actionPerformed(ActionEvent e) {
565                     validateUserInput();
566                 }
567             };
568
569             AbstractAction focusPreviousComponent = new AbstractAction() {
570                 private static final long serialVersionUID = -5286137769378297783L;
571
572                 public void actionPerformed(ActionEvent e) {
573                     KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
574                     manager.focusPreviousComponent();
575                 }
576             };
577             textPane.getInputMap().remove(enterKey);
578             textPane.getInputMap().remove(tabKey);
579             textPane.getInputMap().remove(shiftTabKey);
580             textPane.getInputMap().put(enterKey, validateUserInput);
581             textPane.getInputMap().put(tabKey, focusNextComponent); // input validation will be performed by focusLost
582             textPane.getInputMap().put(shiftTabKey, focusPreviousComponent); // input validation will be performed by focusLost
583         }
584     }
585
586     public void resetBackground() {
587         Color color = (Color) UIManager.getLookAndFeelDefaults().get("TextField.background");
588         if (color != null) {
589             setBackground(color);
590         }
591     }
592
593     public void resetForeground() {
594         Color color = (Color)UIManager.getLookAndFeelDefaults().get("TextField.foreground");
595         if (color != null) {
596             setForeground(color);
597         }
598     }
599 }