in listbox and popupmenu, color must start by #
[scilab.git] / scilab / modules / gui / src / java / org / scilab / modules / gui / bridge / popupmenu / SwingScilabPopupMenu.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) 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.popupmenu;
16
17 import static org.scilab.modules.graphic_objects.graphicObject.GraphicObjectProperties.__GO_UI_MAX__;
18 import static org.scilab.modules.graphic_objects.graphicObject.GraphicObjectProperties.__GO_UI_STRING_COLNB__;
19 import static org.scilab.modules.graphic_objects.graphicObject.GraphicObjectProperties.__GO_UI_STRING__;
20 import static org.scilab.modules.graphic_objects.graphicObject.GraphicObjectProperties.__GO_UI_VALUE__;
21
22 import java.awt.Color;
23 import java.awt.Component;
24 import java.awt.event.ActionEvent;
25 import java.awt.event.ActionListener;
26 import java.io.IOException;
27 import java.util.Arrays;
28
29 import javax.swing.DefaultComboBoxModel;
30 import javax.swing.Icon;
31 import javax.swing.JComboBox;
32 import javax.swing.JLabel;
33 import javax.swing.JList;
34 import javax.swing.ListCellRenderer;
35 import javax.swing.UIManager;
36 import javax.swing.border.Border;
37
38 import org.scilab.modules.commons.gui.FindIconHelper;
39 import org.scilab.modules.graphic_objects.graphicController.GraphicController;
40 import org.scilab.modules.gui.SwingViewObject;
41 import org.scilab.modules.gui.SwingViewWidget;
42 import org.scilab.modules.gui.events.callback.CommonCallBack;
43 import org.scilab.modules.gui.menubar.MenuBar;
44 import org.scilab.modules.gui.popupmenu.SimplePopupMenu;
45 import org.scilab.modules.gui.textbox.TextBox;
46 import org.scilab.modules.gui.toolbar.ToolBar;
47 import org.scilab.modules.gui.utils.ColorBox;
48 import org.scilab.modules.gui.utils.Position;
49 import org.scilab.modules.gui.utils.PositionConverter;
50 import org.scilab.modules.gui.utils.ScilabRelief;
51 import org.scilab.modules.gui.utils.ScilabSwingUtilities;
52 import org.scilab.modules.gui.utils.Size;
53 import org.scilab.modules.gui.utils.SwingScilabListItem;
54
55 /**
56  * Swing implementation for Scilab PopupMenu in GUIs
57  * @author Vincent COUVERT
58  * @author Marouane BEN JELLOUL
59  */
60 public class SwingScilabPopupMenu extends JComboBox implements SwingViewObject, SimplePopupMenu {
61
62     private static final long serialVersionUID = -4366581303317502544L;
63
64     private Integer uid;
65
66     private CommonCallBack callback;
67
68     private ActionListener defaultActionListener;
69
70     private Border defaultBorder = null;
71
72
73     private ListCellRenderer defaultRenderer = null;
74     private ListCellRenderer listRenderer = null;
75
76     /**
77      * Constructor
78      */
79     public SwingScilabPopupMenu() {
80         super();
81
82         defaultRenderer = getRenderer();
83         listRenderer = new ListCellRenderer() {
84             public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
85                 JLabel label = (JLabel) defaultRenderer.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
86
87                 if (value instanceof SwingScilabListItem) {
88                     SwingScilabListItem item = (SwingScilabListItem) value;
89
90                     label.setText(item.toString());
91                     label.setIcon(item.getIcon());
92
93                     //index == -1 is for selected item after click
94                     //so let standard FG and BG
95                     if (index != - 1 && isSelected == false && item.getBackground() != null) {
96                         label.setBackground(item.getBackground());
97                     }
98
99                     if (index != - 1 && isSelected == false && item.getForeground() != null) {
100                         label.setForeground(item.getForeground());
101                     }
102                 } else {
103                     label.setText("");
104                     label.setIcon(null);
105                 }
106                 return label;
107             }
108         };
109
110         setRenderer(listRenderer);
111         /* Bug 3635 fixed: allow arrow keys to browse items */
112         putClientProperty("JComboBox.isTableCellEditor", Boolean.TRUE);
113         defaultActionListener = new ActionListener() {
114             public void actionPerformed(ActionEvent e) {
115                 Double scilabIndices = (double) getUserSelectedIndex();
116                 if (scilabIndices == -1) {
117                     GraphicController.getController().setProperty(uid, __GO_UI_VALUE__, new Double[] {});
118                 } else {
119                     GraphicController.getController().setProperty(uid, __GO_UI_VALUE__, new Double[] {scilabIndices});
120                 }
121
122                 if (callback != null) {
123                     callback.actionPerformed(null);
124                 }
125             }
126         };
127         addActionListener(defaultActionListener);
128     }
129
130     /**
131      * Draws a swing Scilab tab
132      * @see org.scilab.modules.gui.uielement.UIElement#draw()
133      */
134     public void draw() {
135         this.setVisible(true);
136     }
137
138     /**
139      * Gets the dimensions (width and height) of a swing Scilab tab
140      * @return the dimensions of the tab
141      * @see org.scilab.modules.gui.uielement.UIElement#getDims()
142      */
143     public Size getDims() {
144         return new Size(getWidth(), getHeight());
145     }
146
147     /**
148      * Gets the position (X-coordinate and Y-coordinate) of a swing Scilab tab
149      * @return the position of the tab
150      * @see org.scilab.modules.gui.uielement.UIElement#getPosition()
151      */
152     public Position getPosition() {
153         return PositionConverter.javaToScilab(getLocation(), getSize(), getParent());
154     }
155
156     /**
157      * Sets the dimensions (width and height) of a swing Scilab tab
158      * @param newSize the dimensions we want to set to the tab
159      * @see org.scilab.modules.gui.uielement.UIElement#setDims(org.scilab.modules.gui.utils.Size)
160      */
161     public void setDims(Size newSize) {
162         setSize(newSize.getWidth(), newSize.getHeight());
163         doLayout(); /* Needed !! because PopupMenu is badly drawn else */
164     }
165
166     /**
167      * Sets the position (X-coordinate and Y-coordinate) of a swing Scilab tab
168      * @param newPosition the position we want to set to the tab
169      * @see org.scilab.modules.gui.uielement.UIElement#setPosition(org.scilab.modules.gui.utils.Position)
170      */
171     public void setPosition(Position newPosition) {
172         Position javaPosition = PositionConverter.scilabToJava(newPosition, getDims(), getParent());
173         setLocation(javaPosition.getX(), javaPosition.getY());
174     }
175
176     /**
177      * Add a callback to the PopupMenu
178      * @param callback the callback to set.
179      */
180     public void setCallback(CommonCallBack callback) {
181         this.callback = callback;
182     }
183
184     /**
185      * Setter for MenuBar
186      * @param menuBarToAdd the MenuBar associated to the Tab.
187      */
188     public void addMenuBar(MenuBar menuBarToAdd) {
189         /* Unimplemented for PopupMenus */
190         throw new UnsupportedOperationException();
191     }
192
193     /**
194      * Setter for ToolBar
195      * @param toolBarToAdd the ToolBar associated to the Tab.
196      */
197     public void addToolBar(ToolBar toolBarToAdd) {
198         /* Unimplemented for PopupMenus */
199         throw new UnsupportedOperationException();
200     }
201
202     /**
203      * Getter for MenuBar
204      * @return MenuBar: the MenuBar associated to the Tab.
205      */
206     public MenuBar getMenuBar() {
207         /* Unimplemented for PopupMenus */
208         throw new UnsupportedOperationException();
209     }
210
211     /**
212      * Getter for ToolBar
213      * @return ToolBar: the ToolBar associated to the Tab.
214      */
215     public ToolBar getToolBar() {
216         /* Unimplemented for PopupMenus */
217         throw new UnsupportedOperationException();
218     }
219
220     /**
221      * Get the text if the PopupMenu items
222      * @return the items
223      * @see org.scilab.modules.gui.widget.Widget#getText()
224      */
225     public String getText() {
226         /* Unimplemented for PopupMenus */
227         throw new UnsupportedOperationException();
228     }
229
230     /**
231      * Set the text of the PopupMenu items
232      * @param text the text of the items
233      * @see org.scilab.modules.gui.widget.Widget#setText(java.lang.String)
234      */
235     public void setText(String text) {
236         /* Unimplemented for PopupMenus */
237         throw new UnsupportedOperationException();
238     }
239
240     /**
241      * Set the horizontal alignment for the PopupMenu text
242      * @param alignment the value for the alignment (See ScilabAlignment.java)
243      */
244     public void setHorizontalAlignment(String alignment) {
245         // Nothing to do here
246     }
247
248     /**
249      * Set the vertical alignment for the PopupMenu text
250      * @param alignment the value for the alignment (See ScilabAlignment.java)
251      */
252     public void setVerticalAlignment(String alignment) {
253         // Nothing to do here
254     }
255
256     /**
257      * Set the selected index of the PopupMenu
258      * @param index the index of the item to be selected
259      */
260     public void setUserSelectedIndex(int index) {
261         /* Remove the listener to avoid the callback to be executed */
262         removeActionListener(defaultActionListener);
263
264         // Scilab indices in Value begin at 1 and Java indices begin at 0
265         if (index >= 0 && index <= getItemCount()) {
266             setSelectedIndex(index - 1);
267         }
268
269         /* Put back the listener */
270         addActionListener(defaultActionListener);
271     }
272
273     /**
274      * Get the selected index of the PopupMenu
275      * @return the index of the item selected
276      */
277     public int getUserSelectedIndex() {
278         int index = getSelectedIndex();
279         if (index == - 1) {
280             return -1;
281         }
282
283         return index + 1;
284     }
285
286     /**
287      * Get the text of all the PopupMenu items
288      * @return the text items
289      */
290     public String[] getAllItemsText() {
291         String[] retValue = new String[getItemCount()];
292         for (int i = 0; i < getItemCount(); i++) {
293             retValue[i] = getItemAt(i).toString();
294         }
295         return retValue;
296
297     }
298
299     /**
300      * Get the number of items in the PopupMenu
301      * @return the number of items
302      */
303     public int getNumberOfItems() {
304         return getItemCount();
305     }
306
307     /**
308      * Set the text of the PopupMenu items
309      * @param text the text of the items
310      */
311     public void setText(String[] text) {
312         DefaultComboBoxModel model = new DefaultComboBoxModel();
313
314         //get numbers of columns
315         GraphicController controller = GraphicController.getController();
316         Integer nbCol = (Integer) controller.getProperty(getId(), __GO_UI_STRING_COLNB__);
317
318         /* Remove the listener to avoid the callback to be executed */
319         removeActionListener(defaultActionListener);
320
321         boolean tryColorBox = true;
322         boolean tryColor = true;
323         boolean tryIcon = true;
324         int nbRow = text.length / nbCol;
325
326         for (int i = 0; i < nbRow; i++) {
327             Icon icon = null;
328             String str = null;
329             Color background = null;
330             Color foreground = null;
331
332             //4 cols :
333             // - 1st icon or colorBox
334             // - 2nd text
335             // - 3rd BG
336             // - 4th FG
337
338             //3 cols :  2 cases
339             // - 1st icon or colorBox
340             // - 2nd text
341             // - 3rd BG
342             //or
343             // - 1st text
344             // - 2nd BG
345             // - 3rd FG
346
347             //2 cols : 2 cases
348             // - 1st icon or colorBox
349             // - 2nd text
350             //or
351             // - 1st text
352             // - 2nd BG
353
354             if (tryColorBox) { //color
355                 try {
356                     //format #FFFFFF
357                     if (text[i].startsWith("#") == false) {
358                         throw new NumberFormatException();
359                     }
360
361                     Color color = Color.decode(text[i]);
362                     icon = ColorBox.createColorBox(16, 16, color);
363                 } catch (NumberFormatException e) {
364                     tryColorBox = false;
365                     model.removeAllElements();
366                     //restart loop with icon
367                     i = -1;
368                     continue;
369                 }
370             }
371
372             if (tryIcon) {
373                 try {
374                     icon = FindIconHelper.loadIcon(text[i]);
375                 } catch (IOException e) {
376                     tryIcon = false;
377                     model.removeAllElements();
378                     //restart loop with text only
379                     i = -1;
380                     continue;
381                 }
382             }
383
384             if (tryColor) {
385                 try {
386                     int colIndex = 0;
387                     if (tryColorBox || tryIcon) {
388                         colIndex = 1;
389                     }
390
391                     str = text[(nbRow * colIndex) + i];
392                     if (nbCol > (1 + colIndex)) {
393                         if (text[nbRow * (1 + colIndex) + i].startsWith("#") == false) {
394                             throw new NumberFormatException();
395                         }
396
397                         background = Color.decode(text[nbRow * (1 + colIndex) + i]);
398                         if (nbCol > (2 + colIndex)) {
399                             if (text[nbRow * (2 + colIndex) + i].startsWith("#") == false) {
400                                 throw new NumberFormatException();
401                             }
402
403                             foreground = Color.decode(text[nbRow * (2 + colIndex) + i]);
404                         }
405                     }
406
407                     //add item in list box
408                     model.addElement(new SwingScilabListItem(str, icon, background, foreground));
409                 } catch (NumberFormatException e) {
410                     tryColor = false;
411                     model.removeAllElements();
412                     //restart loop with text only
413                     i = -1;
414                     continue;
415                 }
416             } else { //text only
417                 for (int j = 0; j < nbCol; j++) {
418                     model.addElement(new SwingScilabListItem(text[nbRow * j + i], icon, background, foreground));
419                 }
420             }
421         }
422
423         //reset selected index
424         setSelectedIndex(-1);
425         setModel(model);
426         invalidate();
427         //take care to add listener BEFORE set Property to avoid multiple remove and multiple add
428         addActionListener(defaultActionListener);
429     }
430
431     /**
432      * Set the Relief of the PopupMenu
433      * @param reliefType the type of the relief to set (See ScilabRelief.java)
434      */
435     public void setRelief(String reliefType) {
436         if (defaultBorder == null) {
437             defaultBorder = getBorder();
438         }
439         setBorder(ScilabRelief.getBorderFromRelief(reliefType, defaultBorder));
440     }
441
442     /**
443      * Destroy the PopupMenu
444      */
445     public void destroy() {
446         ScilabSwingUtilities.removeFromParent(this);
447     }
448
449     /**
450      * Setter for InfoBar
451      * @param infoBarToAdd the InfoBar associated to the PopupMenu.
452      */
453     public void addInfoBar(TextBox infoBarToAdd) {
454         /* Unimplemented for PopupMenus */
455         throw new UnsupportedOperationException();
456     }
457
458     /**
459      * Getter for InfoBar
460      * @return the InfoBar associated to the PopupMenu.
461      */
462     public TextBox getInfoBar() {
463         /* Unimplemented for PopupMenus */
464         throw new UnsupportedOperationException();
465     }
466
467
468     /**
469      * Set the UID
470      * @param id the UID
471      */
472     public void setId(Integer id) {
473         uid = id;
474     }
475
476     /**
477      * Get the UID
478      * @return the UID
479      */
480     public Integer getId() {
481         return uid;
482     }
483
484     /**
485      * Generic update method
486      * @param property property name
487      * @param value property value
488      */
489     public void update(int property, Object value) {
490         switch (property) {
491             case __GO_UI_STRING__: {
492                 setText((String[]) value);
493                 break;
494             }
495             case __GO_UI_MAX__: {
496                 Integer val = ((Double)value).intValue();
497                 if (val > 1) {
498                     char[] chars = new char[val];
499                     Arrays.fill(chars, '*');
500                     String proto = new String(chars);
501                     setPrototypeDisplayValue(proto);
502                 }
503                 break;
504             }
505             case __GO_UI_VALUE__: {
506                 Double[] doubleValue = ((Double[]) value);
507
508                 //[] or 0 -> no selection
509                 if (doubleValue.length == 0 || doubleValue[0] == 0) {
510                     setUserSelectedIndex(0);
511                     return;
512                 }
513
514                 int[] intValue = new int[doubleValue.length];
515                 for (int k = 0; k < doubleValue.length; k++) {
516                     intValue[k] = doubleValue[k].intValue();
517                 }
518
519                 // Update selected items in the popupmenu
520                 setUserSelectedIndex(intValue[0]);
521                 break;
522             }
523             default: {
524                 SwingViewWidget.update(this, property, value);
525                 break;
526             }
527         }
528     }
529
530     public void resetBackground() {
531         Color color = (Color) UIManager.getLookAndFeelDefaults().get("ComboBox.background");
532         if (color != null) {
533             setBackground(color);
534         }
535     }
536
537     public void resetForeground() {
538         Color color = (Color)UIManager.getLookAndFeelDefaults().get("ComboBox.foreground");
539         if (color != null) {
540             setForeground(color);
541         }
542     }
543 }