fix bug on popupmenu when no item are selected
[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                     Color color = Color.decode(text[i]);
357                     icon = ColorBox.createColorBox(16, 16, color);
358                 } catch (NumberFormatException e) {
359                     tryColorBox = false;
360                     model.removeAllElements();
361                     //restart loop with icon
362                     i = -1;
363                     continue;
364                 }
365             }
366
367             if (tryIcon) {
368                 try {
369                     icon = FindIconHelper.loadIcon(text[i]);
370                 } catch (IOException e) {
371                     tryIcon = false;
372                     model.removeAllElements();
373                     //restart loop with text only
374                     i = -1;
375                     continue;
376                 }
377             }
378
379             if (tryColor) {
380                 try {
381                     int colIndex = 0;
382                     if (tryColorBox || tryIcon) {
383                         colIndex = 1;
384                     }
385
386                     str = text[(nbRow * colIndex) + i];
387                     if (nbCol > (1 + colIndex)) {
388                         background = Color.decode(text[nbRow * (1 + colIndex) + i]);
389                         if (nbCol > (2 + colIndex)) {
390                             foreground = Color.decode(text[nbRow * (2 + colIndex) + i]);
391                         }
392                     }
393
394                     //add item in list box
395                     model.addElement(new SwingScilabListItem(str, icon, background, foreground));
396                 } catch (NumberFormatException e) {
397                     tryColor = false;
398                     model.removeAllElements();
399                     //restart loop with text only
400                     i = -1;
401                     continue;
402                 }
403             } else { //text only
404                 for (int j = 0; j < nbCol; j++) {
405                     model.addElement(new SwingScilabListItem(text[nbRow * j + i], icon, background, foreground));
406                 }
407             }
408         }
409
410         //reset selected index
411         setSelectedIndex(-1);
412         setModel(model);
413         invalidate();
414         //take care to add listener BEFORE set Property to avoid multiple remove and multiple add
415         addActionListener(defaultActionListener);
416     }
417
418     /**
419      * Set the Relief of the PopupMenu
420      * @param reliefType the type of the relief to set (See ScilabRelief.java)
421      */
422     public void setRelief(String reliefType) {
423         if (defaultBorder == null) {
424             defaultBorder = getBorder();
425         }
426         setBorder(ScilabRelief.getBorderFromRelief(reliefType, defaultBorder));
427     }
428
429     /**
430      * Destroy the PopupMenu
431      */
432     public void destroy() {
433         ScilabSwingUtilities.removeFromParent(this);
434     }
435
436     /**
437      * Setter for InfoBar
438      * @param infoBarToAdd the InfoBar associated to the PopupMenu.
439      */
440     public void addInfoBar(TextBox infoBarToAdd) {
441         /* Unimplemented for PopupMenus */
442         throw new UnsupportedOperationException();
443     }
444
445     /**
446      * Getter for InfoBar
447      * @return the InfoBar associated to the PopupMenu.
448      */
449     public TextBox getInfoBar() {
450         /* Unimplemented for PopupMenus */
451         throw new UnsupportedOperationException();
452     }
453
454
455     /**
456      * Set the UID
457      * @param id the UID
458      */
459     public void setId(Integer id) {
460         uid = id;
461     }
462
463     /**
464      * Get the UID
465      * @return the UID
466      */
467     public Integer getId() {
468         return uid;
469     }
470
471     /**
472      * Generic update method
473      * @param property property name
474      * @param value property value
475      */
476     public void update(int property, Object value) {
477         switch (property) {
478             case __GO_UI_STRING__: {
479                 setText((String[]) value);
480                 break;
481             }
482             case __GO_UI_MAX__: {
483                 Integer val = ((Double)value).intValue();
484                 if (val > 1) {
485                     char[] chars = new char[val];
486                     Arrays.fill(chars, '*');
487                     String proto = new String(chars);
488                     setPrototypeDisplayValue(proto);
489                 }
490                 break;
491             }
492             case __GO_UI_VALUE__: {
493                 Double[] doubleValue = ((Double[]) value);
494
495                 //[] or 0 -> no selection
496                 if (doubleValue.length == 0 || doubleValue[0] == 0) {
497                     setUserSelectedIndex(0);
498                     return;
499                 }
500
501                 int[] intValue = new int[doubleValue.length];
502                 for (int k = 0; k < doubleValue.length; k++) {
503                     intValue[k] = doubleValue[k].intValue();
504                 }
505
506                 // Update selected items in the popupmenu
507                 setUserSelectedIndex(intValue[0]);
508                 break;
509             }
510             default: {
511                 SwingViewWidget.update(this, property, value);
512                 break;
513             }
514         }
515     }
516
517     public void resetBackground() {
518         Color color = (Color) UIManager.getLookAndFeelDefaults().get("ComboBox.background");
519         if (color != null) {
520             setBackground(color);
521         }
522     }
523
524     public void resetForeground() {
525         Color color = (Color)UIManager.getLookAndFeelDefaults().get("ComboBox.foreground");
526         if (color != null) {
527             setForeground(color);
528         }
529     }
530 }