* Bug 8378 fixed: `Datatip Menu => Delete last datatip` was useless.
[scilab.git] / scilab / modules / gui / src / java / org / scilab / modules / gui / datatip / DatatipManagerMode.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2012 - Gustavo Barbosa Libotte
4  *
5  * Copyright (C) 2012 - 2016 - Scilab Enterprises
6  *
7  * This file is hereby licensed under the terms of the GNU GPL v2.0,
8  * pursuant to article 5.3.4 of the CeCILL v.2.1.
9  * This file was originally licensed under the terms of the CeCILL v2.1,
10  * and continues to be available under such terms.
11  * For more information, see the COPYING file which you should have received
12  * along with this program.
13  *
14  */
15
16 package org.scilab.modules.gui.datatip;
17
18 import java.awt.Component;
19 import java.awt.Dimension;
20 import java.awt.GridBagConstraints;
21 import java.awt.GridBagLayout;
22 import java.awt.Insets;
23 import java.awt.Point;
24 import java.awt.event.ActionEvent;
25 import java.awt.event.ActionListener;
26 import java.awt.event.MouseEvent;
27 import java.util.HashMap;
28 import java.util.Map;
29 import java.util.Stack;
30
31 import javax.swing.ImageIcon;
32 import javax.swing.JButton;
33 import javax.swing.JDialog;
34 import javax.swing.JFrame;
35 import javax.swing.JMenuItem;
36 import javax.swing.JPanel;
37 import javax.swing.JPopupMenu;
38 import javax.swing.JTextField;
39 import javax.swing.SwingUtilities;
40
41 import org.scilab.modules.commons.gui.FindIconHelper;
42 import org.scilab.modules.localization.Messages;
43 import org.scilab.modules.renderer.utils.AxesHandler;
44 import org.scilab.modules.renderer.utils.ObjectSearcher;
45 import org.scilab.modules.renderer.utils.EntityPicker;
46 import org.scilab.modules.gui.utils.ScilabSwingUtilities;
47 import org.scilab.modules.graphic_objects.graphicController.GraphicController;
48 import org.scilab.modules.graphic_objects.graphicObject.GraphicObjectProperties;
49 import org.scilab.modules.renderer.CallRenderer;
50 import org.scilab.modules.renderer.JoGLView.DrawerVisitor;
51
52 /**
53  * Manage the datatip mode activation
54  * @author Gustavo Barbosa Libotte
55  */
56 public class DatatipManagerMode {
57     private static DatatipManagerMode instance;
58
59     private Integer selectedTip;
60     private Integer markColor;
61
62     private Integer figureUid;
63     private boolean datatipManagerModeStatus;
64
65     private Map<Integer, Stack<Integer>> lastDatatips;
66
67     private static String datatipMessage = Messages.gettext("Left click on the curve creates a datatip and right click on the datatip removes it.");
68
69     public DatatipManagerMode() {
70         instance = this;
71         selectedTip = null;
72         figureUid = null;
73         lastDatatips = new HashMap<Integer, Stack<Integer>>();
74         datatipManagerModeStatus = false;
75     }
76
77     public void setEnabled(boolean b) {
78         datatipManagerModeStatus = b;
79         if (!datatipManagerModeStatus) {
80             GraphicController.getController().setProperty(figureUid, GraphicObjectProperties.__GO_INFO_MESSAGE__, "");
81         } else {
82             GraphicController.getController().setProperty(figureUid, GraphicObjectProperties.__GO_INFO_MESSAGE__, datatipMessage);
83         }
84     }
85
86     /**
87      * Get instance
88      * @return instance
89      */
90     public static DatatipManagerMode getInstance() {
91         return instance;
92     }
93
94     /**
95      * Get the actual color of mark
96      * @return markColor
97      */
98     public Integer getMarkColor() {
99         return markColor;
100     }
101
102     /**
103      * Set mark color
104      * @param newColor scilab color
105      */
106     public void setMarkColor(Integer newColor) {
107         markColor = newColor;
108     }
109
110     /**
111      * Get the datatip's actual manager mode
112      *
113      * @return Datatip manager mode status.
114      */
115     public boolean isEnabled() {
116         return datatipManagerModeStatus;
117     }
118
119     public void setSelectedTip(Integer uid) {
120         markColor = highlightSelected(uid, selectedTip, markColor);
121         selectedTip = uid;
122     }
123
124     public Integer getSelectedTip() {
125         return selectedTip;
126     }
127
128     public void setLastTip(Integer axes, Integer uid) {
129         Stack<Integer> stack = lastDatatips.get(axes);
130         if (stack == null) {
131             stack = new Stack<Integer>();
132             lastDatatips.put(axes, stack);
133         }
134
135         stack.push(uid);
136     }
137
138     public Integer getLastTip(Integer axes) {
139         Stack<Integer> stack = lastDatatips.get(axes);
140         if (stack != null && !stack.isEmpty()) {
141             return stack.pop();
142         }
143
144         return null;
145     }
146
147     public boolean hasLast(Integer[] pos) {
148         Integer axes = AxesHandler.clickedAxes(getFigure(), pos);
149         return axes != null && lastDatatips.containsKey(axes) && !lastDatatips.get(axes).isEmpty();
150     }
151
152     public void enableTranslation(boolean enabled) {
153         DrawerVisitor.getVisitor(figureUid).getInteractionManager().setTranslationEnable(enabled);
154     }
155
156     public boolean isInteractiveZoom() {
157         return DrawerVisitor.getVisitor(figureUid).getInteractionManager().isInteractiveZoom();
158     }
159
160     /**
161      * Set figure uid
162      *
163      * @param uid Figure unique identifier.
164      */
165     public void setFigure(Integer uid) {
166         figureUid = uid;
167     }
168
169     /**
170      * Set figure uid
171      *
172      * @param uid Figure unique identifier.
173      */
174     public Integer getFigure() {
175         return figureUid;
176     }
177
178     /**
179      * Highlight the datatip mark when selected
180      *
181      * @param newTip datatip to highlight.
182      * @param odTip datatip to restore the orginal color.
183      * @param oldTipColor coor to restore.
184      * @return the original datatip mark color.
185      */
186     private Integer highlightSelected(Integer newTip, Integer oldTip, Integer oldTipColor) {
187         Integer color = 0;
188         if (oldTip != null) {
189             GraphicController.getController().setProperty(oldTip, GraphicObjectProperties.__GO_MARK_BACKGROUND__, oldTipColor);
190         }
191         if (newTip != null) {
192             color = (Integer) GraphicController.getController().getProperty(newTip, GraphicObjectProperties.__GO_MARK_BACKGROUND__);
193             GraphicController.getController().setProperty(newTip, GraphicObjectProperties.__GO_MARK_BACKGROUND__, -3);
194         }
195         return color;
196     }
197
198
199     public boolean pickAndHighlight(Integer x, Integer y) {
200         Integer pos[] = {x, y};
201         Integer datatip = (new EntityPicker()).pickDatatip(figureUid, pos);
202         setSelectedTip(datatip);
203         return (datatip != null);
204     }
205
206     public boolean pickAndDelete(Integer x, Integer y) {
207         Integer pos[] = {x, y};
208         Integer datatip = (new EntityPicker()).pickDatatip(figureUid, pos);
209
210         if (datatip != null) {
211             DatatipDelete.deleteDatatip(datatip);
212             if (datatip.equals(selectedTip)) {
213                 selectedTip = null;
214             }
215         }
216         return (datatip != null);
217     }
218
219     /**
220      * On right mouse click, set popup menu visible.
221      * @param event MouseEvent to retrieve click positon in figure.
222      */
223     public void onRightMouseClick(final MouseEvent e) {
224         JPopupMenu menu = new JPopupMenu();
225         JMenuItem deleteAll = new JMenuItem(Messages.gettext("Delete all the datatips"));
226         deleteAll.addActionListener(new ActionListener() {
227             public void actionPerformed(ActionEvent actionEvent) {
228                 deleteAll(new Integer[] {e.getX(), e.getY()});
229             }
230         });
231
232         JMenuItem deleteAllOnCurve = new JMenuItem(Messages.gettext("Delete all the datatips on the selected curve"));
233         deleteAllOnCurve.addActionListener(new ActionListener() {
234             public void actionPerformed(ActionEvent actionEvent) {
235                 deleteAllOnCurve(new Integer[] {e.getX(), e.getY()});
236             }
237         });
238         deleteAllOnCurve.setEnabled(onCurve(new Integer[] {e.getX(), e.getY()}));
239
240         JMenuItem deleteNearest = new JMenuItem(Messages.gettext("Delete the nearest datatip"));
241         deleteNearest.addActionListener(new ActionListener() {
242             public void actionPerformed(ActionEvent actionEvent) {
243                 deleteNearest(new Integer[] {e.getX(), e.getY()});
244             }
245         });
246
247         JMenuItem editDisplayFunction = new JMenuItem(Messages.gettext("Edit curve datatip display function"));
248         editDisplayFunction.addActionListener(new ActionListener() {
249             public void actionPerformed(ActionEvent actionEvent) {
250                 editDisplayFunction(e.getComponent(), new Integer[] {e.getX(), e.getY()});
251             }
252         });
253         editDisplayFunction.setEnabled(onCurve(new Integer[] {e.getX(), e.getY()}));
254
255         JMenuItem enableInterpolation = new JMenuItem(Messages.gettext("Enable interpolation"));
256         enableInterpolation.addActionListener(new ActionListener() {
257             public void actionPerformed(ActionEvent actionEvent) {
258                 enableInterpolation(new Integer[] {e.getX(), e.getY()}, true);
259             }
260         });
261         enableInterpolation.setEnabled(onCurve(new Integer[] {e.getX(), e.getY()}));
262
263         JMenuItem disableInterpolation = new JMenuItem(Messages.gettext("Disable interpolation"));
264         disableInterpolation.addActionListener(new ActionListener() {
265             public void actionPerformed(ActionEvent actionEvent) {
266                 enableInterpolation(new Integer[] {e.getX(), e.getY()}, false);
267             }
268         });
269         disableInterpolation.setEnabled(onCurve(new Integer[] {e.getX(), e.getY()}));
270
271         menu.add(deleteAll);
272         menu.add(deleteAllOnCurve);
273         menu.add(deleteNearest);
274         menu.addSeparator();
275         menu.add(editDisplayFunction);
276         menu.addSeparator();
277         menu.add(enableInterpolation);
278         menu.add(disableInterpolation);
279
280         menu.show(e.getComponent(), e.getX(), e.getY());
281     }
282
283     /**
284      * Enable interpolation
285      * @param pos the mouse position
286      * @param enabled to enable or not tuhe interpolation
287      */
288     private void enableInterpolation(Integer[] pos, boolean enabled) {
289         Integer axes = AxesHandler.clickedAxes(getFigure(), pos);
290         if (axes != null) {
291             final Integer curve = (new EntityPicker()).pick(getFigure(), pos[0], pos[1]);
292             if (curve != null) {
293                 DatatipCreate.datatipSetInterp(curve, enabled);
294             }
295         }
296     }
297
298     /**
299      * Edit display function
300      * @param c the component
301      * @param pos the mouse position
302      */
303     private void editDisplayFunction(Component c, Integer[] pos) {
304         Integer axes = AxesHandler.clickedAxes(getFigure(), pos);
305         if (axes != null) {
306             final Integer curve = (new EntityPicker()).pick(getFigure(), pos[0], pos[1]);
307             if (curve != null) {
308                 String fnc = (String) GraphicController.getController().getProperty(curve, GraphicObjectProperties.__GO_DATATIP_DISPLAY_FNC__);
309                 JFrame parentWin = (JFrame) SwingUtilities.getAncestorOfClass(JFrame.class, c);
310                 final JDialog dialog = new JDialog(parentWin);
311                 dialog.setIconImage(new ImageIcon(FindIconHelper.findIcon("datatips")).getImage());
312                 dialog.setTitle(Messages.gettext("Display function name"));
313                 dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
314                 ScilabSwingUtilities.closeOnEscape(dialog);
315                 boolean isWin = System.getProperty("os.name").toLowerCase().contains("windows");
316                 JPanel main = new JPanel();
317                 final JTextField text = new JTextField(30);
318                 text.setText(fnc);
319                 JButton ok = new JButton(Messages.gettext("OK"));
320                 ActionListener valid = new ActionListener() {
321                     public void actionPerformed(ActionEvent actionEvent) {
322                         String s = text.getText();
323                         if (s != null && !s.isEmpty()) {
324                             GraphicController.getController().setProperty(curve, GraphicObjectProperties.__GO_DATATIP_DISPLAY_FNC__, s);
325                         }
326                         dialog.dispose();
327                     }
328                 };
329
330                 ok.addActionListener(valid);
331                 text.addActionListener(valid);
332
333                 JButton cancel = new JButton(Messages.gettext("Cancel"));
334                 cancel.addActionListener(new ActionListener() {
335                     public void actionPerformed(ActionEvent actionEvent) {
336                         dialog.dispose();
337                     }
338                 });
339
340                 main.setLayout(new GridBagLayout());
341                 GridBagConstraints gbc = new GridBagConstraints();
342
343                 gbc.gridx = gbc.gridy = 0;
344                 gbc.gridwidth = 3;
345                 gbc.weightx = gbc.weighty = 1;
346                 gbc.fill = GridBagConstraints.HORIZONTAL;
347                 gbc.insets = new Insets(10, 5, 5, 5);
348                 main.add(text, gbc);
349
350                 gbc.gridx = 0;
351                 gbc.gridy = 1;
352                 gbc.weighty = 0;
353                 gbc.gridwidth = 1;
354                 gbc.fill = GridBagConstraints.HORIZONTAL;
355                 main.add(new JPanel(), gbc);
356
357                 gbc.gridx = isWin ? 1 : 2;
358                 gbc.weightx = gbc.weighty = 0;
359                 gbc.anchor = GridBagConstraints.LINE_START;
360                 gbc.fill = GridBagConstraints.NONE;
361                 gbc.insets = new Insets(0, 0, 5, 5);
362                 main.add(ok, gbc);
363
364                 gbc.gridx = isWin ? 2 : 1;
365                 main.add(cancel, gbc);
366
367                 dialog.setContentPane(main);
368                 dialog.pack();
369
370                 Point loc = parentWin.getLocationOnScreen();
371                 Dimension pdim = parentWin.getSize();
372                 Dimension ddim = dialog.getSize();
373                 dialog.setLocation(loc.x + (pdim.width - ddim.width) / 2, loc.y + (pdim.height - ddim.height) / 2);
374                 dialog.setVisible(true);
375
376                 text.requestFocus();
377             }
378         }
379     }
380
381     /**
382      * Delete the nearest datatip
383      * @param pos the mouse position
384      */
385     private void deleteNearest(Integer[] pos) {
386         Integer axes = AxesHandler.clickedAxes(getFigure(), pos);
387         if (axes != null) {
388             Integer[] datatips = (new ObjectSearcher()).search(axes, GraphicObjectProperties.__GO_DATATIP__, true);
389             if (datatips != null) {
390                 int x = pos[0];
391                 int y = pos[1];
392                 double min = Double.POSITIVE_INFINITY;
393                 int datatip = -1;
394                 for (int i : datatips) {
395                     Double[] tip_pos = (Double[]) GraphicController.getController().getProperty(i, GraphicObjectProperties.__GO_DATATIP_DATA__);
396                     double[] point = new double[] {tip_pos[0], tip_pos[1], tip_pos[2]};
397                     double[] pix = CallRenderer.getPixelFrom3dCoordinates(axes, point);
398                     double d = Math.hypot(x - pix[0], y - pix[1]);
399                     if (d < min) {
400                         min = d;
401                         datatip = i;
402                     }
403                 }
404
405                 if (datatip != -1) {
406                     DatatipDelete.deleteDatatip(datatip);
407                 }
408             }
409         }
410     }
411
412     /**
413      * Delete all datatips on a curve
414      * @param pos the mouse position
415      */
416     private void deleteAllOnCurve(Integer[] pos) {
417         Integer axes = AxesHandler.clickedAxes(getFigure(), pos);
418         if (axes != null) {
419             Integer curve = (new EntityPicker()).pick(getFigure(), pos[0], pos[1]);
420             if (curve != null) {
421                 Integer[] datatips = (Integer[]) GraphicController.getController().getProperty(curve, GraphicObjectProperties.__GO_DATATIPS__);
422                 if (datatips != null) {
423                     for (int i : datatips) {
424                         DatatipDelete.deleteDatatip(i);
425                     }
426                 }
427             }
428         }
429     }
430
431     /**
432      * Delete all datatips
433      * @param pos the mouse position
434      */
435     private void deleteAll(Integer[] pos) {
436         Integer axes = AxesHandler.clickedAxes(getFigure(), pos);
437         if (axes != null) {
438             Integer[] datatips = (new ObjectSearcher()).search(axes, GraphicObjectProperties.__GO_DATATIP__, true);
439             if (datatips != null) {
440                 for (int i : datatips) {
441                     DatatipDelete.deleteDatatip(i);
442                 }
443             }
444         }
445     }
446
447     /**
448      * Check if we are on a curve
449      * @param pos the mouse position
450      * @return true if the click occurred on a curve
451      */
452     private boolean onCurve(Integer[] pos) {
453         Integer axes = AxesHandler.clickedAxes(getFigure(), pos);
454         if (axes != null) {
455             Integer curve = (new EntityPicker()).pick(getFigure(), pos[0], pos[1]);
456             return curve != null;
457         }
458
459         return false;
460     }
461 }