Re-apply doc after Font setting
[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             doc.setParagraphAttributes(0, doc.getLength() - 1, docAttributes, true);
463         }
464     }
465
466     public void setFont(Font font) {
467         setEditFont(font);
468     }
469
470     /**
471      * Generic update method
472      * @param property property name
473      * @param value property value
474      */
475     public void update(int property, Object value) {
476         GraphicController controller = GraphicController.getController();
477
478         switch (property) {
479             case __GO_UI_MAX__: {
480                 double min = ((Double) controller.getProperty(uid, __GO_UI_MIN__));
481                 double max = ((Double) controller.getProperty(uid, __GO_UI_MAX__));
482                 if (max - min > 1.0) {
483                     setMultiLineText(true);
484                 } else {
485                     setMultiLineText(false);
486                 }
487                 // Force String update
488                 update(__GO_UI_STRING__, GraphicController.getController().getProperty(uid, __GO_UI_STRING__));
489                 break;
490             }
491             case __GO_UI_MIN__: {
492                 Double min = ((Double) value);
493                 Double max = ((Double) controller.getProperty(uid, __GO_UI_MAX__));
494                 if (max - min > 1.0) {
495                     setMultiLineText(true);
496                 } else {
497                     setMultiLineText(false);
498                 }
499                 // Force String update
500                 update(__GO_UI_STRING__, GraphicController.getController().getProperty(uid, __GO_UI_STRING__));
501                 break;
502             }
503             case __GO_UI_STRING__: {
504                 double min = ((Double) controller.getProperty(uid, __GO_UI_MIN__));
505                 double max = ((Double) controller.getProperty(uid, __GO_UI_MAX__));
506                 if (max - min > 1.0) {
507                     setText((String[]) value);
508                     setMultiLineText(true);
509                 } else {
510                     setText(((String[]) value)[0]);
511                     setMultiLineText(false);
512                 }
513                 break;
514             }
515             case __GO_UI_SCROLLABLE__ : {
516                 setScrollable((Boolean)value);
517                 break;
518             }
519             default: {
520                 SwingViewWidget.update(this, property, value);
521                 break;
522             }
523         }
524     }
525
526     public void setScrollable(Boolean scrollable) {
527         if (scrollable) {
528             setViewportView(noWrapPanel);
529             noWrapPanel.add(textPane);
530         } else {
531             setViewportView(textPane);
532         }
533     }
534
535     public String getText() {
536         return textPane.getText();
537     }
538
539     public void setMultiLineText(boolean enable) {
540         KeyStroke enterKey = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
541         KeyStroke tabKey = KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0);
542         KeyStroke shiftTabKey = KeyStroke.getKeyStroke(KeyEvent.VK_TAB, KeyEvent.SHIFT_DOWN_MASK);
543         if (enable) {
544             setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
545             textPane.getInputMap().remove(enterKey);
546             textPane.getInputMap().remove(tabKey);
547             textPane.getInputMap().remove(shiftTabKey);
548             textPane.getInputMap().put(enterKey, enterKeyAction);
549             textPane.getInputMap().put(tabKey, tabKeyAction);
550             textPane.getInputMap().put(tabKey, shiftTabKeyAction);
551         } else {
552             setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
553             AbstractAction focusNextComponent = new AbstractAction() {
554                 private static final long serialVersionUID = -5286137769378297783L;
555
556                 public void actionPerformed(ActionEvent e) {
557                     KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
558                     manager.focusNextComponent();
559                 }
560             };
561
562             AbstractAction validateUserInput = new AbstractAction() {
563                 private static final long serialVersionUID = -5286137769378297783L;
564
565                 public void actionPerformed(ActionEvent e) {
566                     validateUserInput();
567                 }
568             };
569
570             AbstractAction focusPreviousComponent = new AbstractAction() {
571                 private static final long serialVersionUID = -5286137769378297783L;
572
573                 public void actionPerformed(ActionEvent e) {
574                     KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
575                     manager.focusPreviousComponent();
576                 }
577             };
578             textPane.getInputMap().remove(enterKey);
579             textPane.getInputMap().remove(tabKey);
580             textPane.getInputMap().remove(shiftTabKey);
581             textPane.getInputMap().put(enterKey, validateUserInput);
582             textPane.getInputMap().put(tabKey, focusNextComponent); // input validation will be performed by focusLost
583             textPane.getInputMap().put(shiftTabKey, focusPreviousComponent); // input validation will be performed by focusLost
584         }
585     }
586
587     public void resetBackground() {
588         Color color = (Color) UIManager.getLookAndFeelDefaults().get("TextField.background");
589         if (color != null) {
590             setBackground(color);
591         }
592     }
593
594     public void resetForeground() {
595         Color color = (Color)UIManager.getLookAndFeelDefaults().get("TextField.foreground");
596         if (color != null) {
597             setForeground(color);
598         }
599     }
600 }