in listbox and popupmenu, color must start by #
[scilab.git] / scilab / modules / gui / src / java / org / scilab / modules / gui / bridge / listbox / SwingScilabListBox.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2007 - INRIA - Vincent Couvert
4  * Copyright (C) 2007 - INRIA - Marouane BEN JELLOUL
5  * Copyright (C) 2010-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.listbox;
16
17 import static org.scilab.modules.graphic_objects.graphicObject.GraphicObjectProperties.__GO_UI_BACKGROUNDCOLOR__;
18 import static org.scilab.modules.graphic_objects.graphicObject.GraphicObjectProperties.__GO_UI_LISTBOXTOP__;
19 import static org.scilab.modules.graphic_objects.graphicObject.GraphicObjectProperties.__GO_UI_MAX__;
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_COLNB__;
22 import static org.scilab.modules.graphic_objects.graphicObject.GraphicObjectProperties.__GO_UI_STRING__;
23 import static org.scilab.modules.graphic_objects.graphicObject.GraphicObjectProperties.__GO_UI_VALUE__;
24
25 import java.awt.Color;
26 import java.awt.Component;
27 import java.awt.Font;
28 import java.awt.event.AdjustmentEvent;
29 import java.awt.event.AdjustmentListener;
30 import java.io.IOException;
31
32 import javax.swing.DefaultListModel;
33 import javax.swing.Icon;
34 import javax.swing.JLabel;
35 import javax.swing.JList;
36 import javax.swing.JScrollPane;
37 import javax.swing.ListCellRenderer;
38 import javax.swing.ListSelectionModel;
39 import javax.swing.UIManager;
40 import javax.swing.border.Border;
41 import javax.swing.event.ListSelectionEvent;
42 import javax.swing.event.ListSelectionListener;
43
44 import org.scilab.modules.commons.gui.FindIconHelper;
45 import org.scilab.modules.graphic_objects.graphicController.GraphicController;
46 import org.scilab.modules.gui.SwingViewObject;
47 import org.scilab.modules.gui.SwingViewWidget;
48 import org.scilab.modules.gui.events.callback.CommonCallBack;
49 import org.scilab.modules.gui.listbox.SimpleListBox;
50 import org.scilab.modules.gui.menubar.MenuBar;
51 import org.scilab.modules.gui.textbox.TextBox;
52 import org.scilab.modules.gui.toolbar.ToolBar;
53 import org.scilab.modules.gui.utils.ColorBox;
54 import org.scilab.modules.gui.utils.Position;
55 import org.scilab.modules.gui.utils.PositionConverter;
56 import org.scilab.modules.gui.utils.ScilabRelief;
57 import org.scilab.modules.gui.utils.ScilabSwingUtilities;
58 import org.scilab.modules.gui.utils.Size;
59 import org.scilab.modules.gui.utils.SwingScilabListItem;
60
61 /**
62  * Swing implementation for Scilab ListBox in GUIs
63  * @author Vincent COUVERT
64  * @author Marouane BEN JELLOUL
65  */
66 public class SwingScilabListBox extends JScrollPane implements SwingViewObject, SimpleListBox {
67
68     private static final long serialVersionUID = 3507396207331058895L;
69
70     private static final int COLORS_COEFF = 255;
71
72     private Integer uid;
73
74     private Border defaultBorder = null;
75
76     private CommonCallBack callback;
77
78     private ListSelectionListener listListener;
79
80     private AdjustmentListener adjustmentListener;
81
82     /**
83      * the JList we use
84      */
85     private JList list;
86
87     private ListCellRenderer defaultRenderer = null;
88     private ListCellRenderer listRenderer = null;
89
90     /**
91      * Constructor
92      */
93     public SwingScilabListBox() {
94         super();
95         getViewport().add(getList());
96         defaultRenderer = getList().getCellRenderer();
97
98         listRenderer = new ListCellRenderer() {
99             public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
100                 JLabel label = (JLabel) defaultRenderer.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
101
102                 if (value instanceof SwingScilabListItem) {
103                     SwingScilabListItem item = (SwingScilabListItem) value;
104                     label.setText(item.toString());
105                     label.setIcon(item.getIcon());
106
107                     if (isSelected == false && item.getBackground() != null) {
108                         label.setBackground(item.getBackground());
109                     }
110
111                     if (isSelected == false && item.getForeground() != null) {
112                         label.setForeground(item.getForeground());
113                     }
114                 }
115                 return label;
116             }
117         };
118
119         getList().setCellRenderer(listRenderer);
120
121         listListener = new ListSelectionListener() {
122             public void valueChanged(ListSelectionEvent e) {
123
124                 //value not ready
125                 if (e.getValueIsAdjusting()) {
126                     return;
127                 }
128
129                 // Scilab indices in Value begin at 1 and Java indices begin at 0
130                 int[] javaIndices = getList().getSelectedIndices().clone();
131                 Double[] scilabIndices = new Double[javaIndices.length];
132                 for (int i = 0; i < getList().getSelectedIndices().length; i++) {
133                     scilabIndices[i] = (double) javaIndices[i] + 1;
134                 }
135
136                 GraphicController.getController().setProperty(uid, __GO_UI_VALUE__, scilabIndices);
137                 if (callback != null) {
138                     callback.actionPerformed(null);
139                 }
140             }
141         };
142         getList().addListSelectionListener(listListener);
143
144         adjustmentListener = new AdjustmentListener() {
145             public void adjustmentValueChanged(AdjustmentEvent arg0) {
146                 int listboxtopValue = getList().getUI().locationToIndex(getList(), getViewport().getViewPosition()) + 1;
147                 GraphicController.getController().setProperty(uid, __GO_UI_LISTBOXTOP__, new Integer[] { listboxtopValue });
148             }
149         };
150         getVerticalScrollBar().addAdjustmentListener(adjustmentListener);
151     }
152
153     /**
154      * To get the Background color of the element.
155      * @return color the Color
156      */
157     public Color getBackground() {
158         return getList().getBackground();
159     }
160
161     /**
162      * To get the Font of the element.
163      * @return font the Font
164      */
165     public Font getFont() {
166         return getList().getFont();
167     }
168
169     /**
170      * To get the Foreground color of the element.
171      * @return color the Color
172      */
173     public Color getForeground() {
174         return getList().getForeground();
175     }
176
177     /**
178      * To set the Background color of the element.
179      * @param color the Color
180      */
181     public void setListBackground(Color color) {
182         getList().setBackground(color);
183     }
184
185     /**
186      * To set the Font of the element.
187      * @param font the Font
188      */
189     public void setFont(Font font) {
190         getList().setFont(font);
191     }
192
193     /**
194      * To set the Foreground color of the element.
195      * @param color the Color
196      */
197     public void setForeground(Color color) {
198         getList().setForeground(color);
199     }
200
201     /**
202      * Draws a swing Scilab tab
203      * @see org.scilab.modules.gui.uielement.UIElement#draw()
204      */
205     public void draw() {
206         this.setVisible(true);
207         this.doLayout();
208     }
209
210     /**
211      * Gets the dimensions (width and height) of a swing Scilab tab
212      * @return the dimensions of the tab
213      * @see org.scilab.modules.gui.uielement.UIElement#getDims()
214      */
215     public Size getDims() {
216         return new Size(getWidth(), getHeight());
217     }
218
219     /**
220      * Gets the position (X-coordinate and Y-coordinate) of a swing Scilab tab
221      * @return the position of the tab
222      * @see org.scilab.modules.gui.uielement.UIElement#getPosition()
223      */
224     public Position getPosition() {
225         return PositionConverter.javaToScilab(getLocation(), getSize(), getParent());
226     }
227
228     /**
229      * Sets the dimensions (width and height) of a swing Scilab tab
230      * @param newSize the dimensions we want to set to the tab
231      * @see org.scilab.modules.gui.uielement.UIElement#setDims(org.scilab.modules.gui.utils.Size)
232      */
233     public void setDims(Size newSize) {
234         setSize(newSize.getWidth(), newSize.getHeight());
235     }
236
237     /**
238      * Sets the position (X-coordinate and Y-coordinate) of a swing Scilab tab
239      * @param newPosition the position we want to set to the tab
240      * @see org.scilab.modules.gui.uielement.UIElement#setPosition(org.scilab.modules.gui.utils.Position)
241      */
242     public void setPosition(Position newPosition) {
243         Position javaPosition = PositionConverter.scilabToJava(newPosition, getDims(), getParent());
244         setLocation(javaPosition.getX(), javaPosition.getY());
245     }
246
247     /**
248      * Sets the visibility status of an UIElement
249      * @param newVisibleState the visibility status we want to set for the
250      * UIElement (true if the UIElement is visible, false if not)
251      */
252     public void setVisible(boolean newVisibleState) {
253         super.setVisible(newVisibleState);
254         list.setVisible(newVisibleState);
255     }
256
257     /**
258      * Sets the enable status of an UIElement
259      * @param newEnableState the enable status we want to set for the UIElement
260      * (true if the UIElement is enabled, false if not)
261      */
262     public void setEnabled(boolean newEnableState) {
263         if (newEnableState != super.isEnabled()) {
264             super.setEnabled(newEnableState);
265             getList().setEnabled(newEnableState);
266             if (newEnableState) {
267                 if (listListener != null) {
268                     getList().addListSelectionListener(listListener);
269                 }
270             } else {
271                 if (listListener != null) {
272                     getList().removeListSelectionListener(listListener);
273                 }
274             }
275         }
276     }
277
278     /**
279      * Add a callback to the CheckBox
280      * @param cb the callback to set.
281      */
282     public void setCallback(CommonCallBack cb) {
283         this.callback = cb;
284     }
285
286     /**
287      * Setter for MenuBar
288      * @param menuBarToAdd the MenuBar associated to the Tab.
289      */
290     public void addMenuBar(MenuBar menuBarToAdd) {
291         /* Unimplemented for ListBoxes */
292         throw new UnsupportedOperationException();
293     }
294
295     /**
296      * Setter for ToolBar
297      * @param toolBarToAdd the ToolBar associated to the Tab.
298      */
299     public void addToolBar(ToolBar toolBarToAdd) {
300         /* Unimplemented for ListBoxes */
301         throw new UnsupportedOperationException();
302     }
303
304     /**
305      * Getter for MenuBar
306      * @return MenuBar: the MenuBar associated to the Tab.
307      */
308     public MenuBar getMenuBar() {
309         /* Unimplemented for ListBoxes */
310         throw new UnsupportedOperationException();
311     }
312
313     /**
314      * Getter for ToolBar
315      * @return ToolBar: the ToolBar associated to the Tab.
316      */
317     public ToolBar getToolBar() {
318         /* Unimplemented for ListBoxes */
319         throw new UnsupportedOperationException();
320     }
321
322     /**
323      * Get the first item text
324      * @return the items
325      * @see org.scilab.modules.gui.widget.Widget#getText()
326      */
327     public String getText() {
328         /* Unimplemented for ListBoxes */
329         throw new UnsupportedOperationException();
330     }
331
332     /**
333      * Get the text of all the list items
334      * @return the items
335      * @see org.scilab.modules.gui.listbox.ListBox#getAllItemsText()
336      */
337     public String[] getAllItemsText() {
338         String[] retValue = new String[getList().getModel().getSize()];
339         for (int i = 0; i < getList().getModel().getSize(); i++) {
340             retValue[i] = getList().getModel().getElementAt(i).toString();
341         }
342         return retValue;
343     }
344
345     /**
346      * Get the number of items in the list
347      * @return the number of items
348      * @see org.scilab.modules.gui.listbox.ListBox#getNumberOfItems()
349      */
350     public int getNumberOfItems() {
351         return getList().getModel().getSize();
352     }
353
354     /**
355      * Set the text of the list items
356      * @param text the text of the items
357      * @see org.scilab.modules.gui.widget.Widget#setText(java.lang.String)
358      */
359     public void setText(String text) {
360         DefaultListModel model = new DefaultListModel();
361         model.addElement(text);
362         getList().setModel(model);
363         revalidate();
364     }
365
366     /**
367      * Set the text of the list items
368      * @param text the text of the items
369      * @see org.scilab.modules.gui.widget.Widget#setText(java.lang.String)
370      */
371     public void setText(String[] text) {
372         DefaultListModel model = new DefaultListModel();
373
374         // check numbers of columns
375         GraphicController controller = GraphicController.getController();
376         Integer nbCol = (Integer) controller.getProperty(getId(), __GO_UI_STRING_COLNB__);
377
378         /* Remove the listener to avoid the callback to be executed */
379         getList().removeListSelectionListener(listListener);
380
381         boolean tryColorBox = true;
382         boolean tryColor = true;
383         boolean tryIcon = true;
384         int nbRow = text.length / nbCol;
385
386         for (int i = 0; i < nbRow; i++) {
387             Icon icon = null;
388             String str = null;
389             Color background = null;
390             Color foreground = null;
391
392             //4 cols :
393             // - 1st icon or colorBox
394             // - 2nd text
395             // - 3rd BG
396             // - 4th FG
397
398             //3 cols :  2 cases
399             // - 1st icon or colorBox
400             // - 2nd text
401             // - 3rd BG
402             //or
403             // - 1st text
404             // - 2nd BG
405             // - 3rd FG
406
407             //2 cols : 2 cases
408             // - 1st icon or colorBox
409             // - 2nd text
410             //or
411             // - 1st text
412             // - 2nd BG
413
414             if (tryColorBox) { //color
415                 try {
416                     //format #FFFFFF
417                     if (text[i].startsWith("#") == false) {
418                         throw new NumberFormatException();
419                     }
420
421                     Color color = Color.decode(text[i]);
422                     icon = ColorBox.createColorBox(16, 16, color);
423                 } catch (NumberFormatException e) {
424                     tryColorBox = false;
425                     model.clear();
426                     //restart loop with icon
427                     i = -1;
428                     continue;
429                 }
430             }
431
432             if (tryIcon) {
433                 try {
434                     icon = FindIconHelper.loadIcon(text[i]);
435                 } catch (IOException e) {
436                     tryIcon = false;
437                     model.clear();
438                     //restart loop with text only
439                     i = -1;
440                     continue;
441                 }
442             }
443
444             if (tryColor) {
445                 try {
446                     int colIndex = 0;
447                     if (tryColorBox || tryIcon) {
448                         colIndex = 1;
449                     }
450
451                     str = text[(nbRow * colIndex) + i];
452                     if (nbCol > (1 + colIndex)) {
453                         if (text[nbRow * (1 + colIndex) + i].startsWith("#") == false) {
454                             throw new NumberFormatException();
455                         }
456
457                         background = Color.decode(text[nbRow * (1 + colIndex) + i]);
458                         if (nbCol > (2 + colIndex)) {
459                             if (text[nbRow * (2 + colIndex) + i].startsWith("#") == false) {
460                                 throw new NumberFormatException();
461                             }
462                             foreground = Color.decode(text[nbRow * (2 + colIndex) + i]);
463                         }
464                     }
465
466                     //add item in list box
467                     model.addElement(new SwingScilabListItem(str, icon, background, foreground));
468                 } catch (NumberFormatException e) {
469                     tryColor = false;
470                     model.clear();
471                     //restart loop with text only
472                     i = -1;
473                     continue;
474                 }
475             } else { //text only
476                 for (int j = 0; j < nbCol; j++) {
477                     model.addElement(new SwingScilabListItem(text[nbRow * j + i], icon, background, foreground));
478                 }
479             }
480         }
481
482         //reset selected index
483         getList().setSelectedIndex(-1);
484         getList().setModel(model);
485         invalidate();
486         //take care to add listener BEFORE set Property to avoid multiple remove and multiple add
487         getList().addListSelectionListener(listListener);
488     }
489
490     /**
491      * Set the horizontal alignment for the ListBox text
492      * @param alignment the value for the alignment (See ScilabAlignment.java)
493      */
494     public void setHorizontalAlignment(String alignment) {
495         // Nothing to do here
496     }
497
498     /**
499      * Set the vertical alignment for the ListBox text
500      * @param alignment the value for the alignment (See ScilabAlignment.java)
501      */
502     public void setVerticalAlignment(String alignment) {
503         // Nothing to do here
504     }
505
506     /**
507      * Set if more than one item can be selected in a ListBox
508      * @param status true if multiple selection is enabled
509      */
510     public void setMultipleSelectionEnabled(boolean status) {
511         if (status) {
512             getList().setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
513         } else {
514             getList().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
515         }
516     }
517
518     /**
519      * Set the selected indices of the ListBox
520      * @param indices the indices of the items to be selected
521      */
522     public void setSelectedIndices(int[] indices) {
523         // Scilab indices in Value begin at 1 and Java indices begin at 0
524         int[] javaIndices = indices.clone();
525         for (int i = 0; i < javaIndices.length; i++) {
526             javaIndices[i] = javaIndices[i] - 1;
527         }
528
529         /* Remove the listener to avoid the callback to be executed */
530         if (listListener != null) {
531             getList().removeListSelectionListener(listListener);
532         }
533
534         getList().setSelectedIndices(javaIndices);
535
536         /* Put back the listener */
537         if (listListener != null) {
538             getList().addListSelectionListener(listListener);
539         }
540     }
541
542     /**
543      * Get the selected indices of the ListBox
544      * @return the indices of the items selected
545      */
546     public int[] getSelectedIndices() {
547         // Scilab indices in Value begin at 1 and Java indices begin at 0
548         int[] javaIndices = getList().getSelectedIndices().clone();
549         int[] scilabIndices = javaIndices.clone();
550         for (int i = 0; i < getList().getSelectedIndices().length; i++) {
551             scilabIndices[i] = scilabIndices[i] + 1;
552         }
553         return scilabIndices;
554     }
555
556     /**
557      * Get the number of items selected in the ListBox
558      * @return the number of items selected
559      */
560     public int getSelectionSize() {
561         return getList().getSelectedIndices().length;
562     }
563
564     /**
565      * Get or create the list component
566      * @return the list
567      */
568     private JList getList() {
569         if (list == null) {
570             list = new JList();
571             list.setLayoutOrientation(JList.VERTICAL);
572             list.setModel(new DefaultListModel());
573         }
574         return list;
575     }
576
577     /**
578      * Set the Relief of the ListBox
579      * @param reliefType the type of the relief to set (See ScilabRelief.java)
580      */
581     public void setRelief(String reliefType) {
582         if (defaultBorder == null) {
583             defaultBorder = getBorder();
584         }
585         setBorder(ScilabRelief.getBorderFromRelief(reliefType, defaultBorder));
586     }
587
588     /**
589      * Destroy the ListBox
590      */
591     public void destroy() {
592         ScilabSwingUtilities.removeFromParent(this);
593     }
594
595     /**
596      * Setter for InfoBar
597      * @param infoBarToAdd the InfoBar associated to the ListBox.
598      */
599     public void addInfoBar(TextBox infoBarToAdd) {
600         /* Unimplemented for ListBoxes */
601         throw new UnsupportedOperationException();
602     }
603
604     /**
605      * Getter for InfoBar
606      * @return the InfoBar associated to the ListBox.
607      */
608     public TextBox getInfoBar() {
609         /* Unimplemented for ListBoxes */
610         throw new UnsupportedOperationException();
611     }
612
613     /**
614      * Adjusts the view so that the element given by index is displayed at the
615      * top of the ListBox.
616      * @param index the index of the element to be displayed at the top of the
617      * ListBox.
618      */
619     public void setListBoxTop(int index) {
620         getVerticalScrollBar().removeAdjustmentListener(adjustmentListener);
621         if (index > 0 & index != getListBoxTop()) {
622             getViewport().setViewPosition(getList().getUI().indexToLocation(getList(), index - 1));
623             doLayout();
624         }
625         getVerticalScrollBar().addAdjustmentListener(adjustmentListener);
626     }
627
628     /**
629      * Gets the index of the element displayed at the top of the ListBox
630      * @return the index of the element displayed at the top of the ListBox
631      */
632     public int getListBoxTop() {
633         return getList().getUI().locationToIndex(getList(), getViewport().getViewPosition()) + 1;
634     }
635
636     /**
637      * Set the UID
638      * @param id the UID
639      */
640     public void setId(Integer id) {
641         uid = id;
642     }
643
644     /**
645      * Get the UID
646      * @return the UID
647      */
648     public Integer getId() {
649         return uid;
650     }
651
652     /**
653      * Generic update method
654      * @param property property name
655      * @param value property value
656      */
657     public void update(int property, Object value) {
658         GraphicController controller = GraphicController.getController();
659         switch (property) {
660             case __GO_UI_VALUE__: {
661                 Double[] indexes = (Double[]) value;
662                 int[] index = new int[indexes.length];
663                 for (int i = 0; i < indexes.length; i++) {
664                     index[i] = indexes[i].intValue();
665                 }
666                 setSelectedIndices(index);
667                 break;
668             }
669             case __GO_UI_BACKGROUNDCOLOR__: {
670                 Double[] allColors = ((Double[]) value);
671                 if (allColors[0] != -1) {
672                     setListBackground(new Color((int) (allColors[0] * COLORS_COEFF), (int) (allColors[1] * COLORS_COEFF), (int) (allColors[2] * COLORS_COEFF)));
673                 } else {
674                     resetBackground();
675                 }
676                 break;
677             }
678             case __GO_UI_STRING__: {
679                 // Listboxes manage string vectors
680                 setText((String[]) value);
681                 break;
682             }
683             case __GO_UI_MAX__: {
684                 Double maxValue = ((Double) value);
685                 // Enable/Disable multiple selection
686                 double minValue = (Double) controller.getProperty(uid, __GO_UI_MIN__);
687                 setMultipleSelectionEnabled(maxValue - minValue > 1);
688                 break;
689             }
690             case __GO_UI_MIN__: {
691                 Double minValue = ((Double) value);
692                 // Enable/Disable multiple selection
693                 Double maxValue = (Double) controller.getProperty(uid, __GO_UI_MAX__);
694                 setMultipleSelectionEnabled(maxValue - minValue > 1);
695                 break;
696             }
697             case __GO_UI_LISTBOXTOP__: {
698                 Integer[] listboxtopValue = ((Integer[]) value);
699                 if (listboxtopValue.length > 0) {
700                     setListBoxTop(listboxtopValue[0]);
701                 }
702                 break;
703             }
704             default: {
705                 SwingViewWidget.update(this, property, value);
706                 break;
707             }
708         }
709     }
710
711     public void resetBackground() {
712         Color color = (Color) UIManager.getLookAndFeelDefaults().get("List.background");
713         if (color != null) {
714             getList().setBackground(color);
715         }
716     }
717
718     public void resetForeground() {
719         Color color = (Color) UIManager.getLookAndFeelDefaults().get("List.foreground");
720         if (color != null) {
721             getList().setForeground(color);
722         }
723     }
724 }