Warning: Can't use blame annotator:
svn blame failed on trunk/src/scriptbuilder/gui/drawers/RangeSliderUI.java: ("Can't find a temporary directory: Internal error", 20014)

source: tmcsimulator-scriptbuilder/trunk/src/scriptbuilder/gui/drawers/RangeSliderUI.java @ 26

Revision 26, 23.5 KB checked in by jdalbey, 9 years ago (diff)

RangeSliderUI.java: Added popup menu (with stubs for actions)

RevLine 
1package scriptbuilder.gui.drawers;
2
3import java.awt.Color;
4import java.awt.Dimension;
5import java.awt.Graphics;
6import java.awt.Graphics2D;
7import java.awt.Rectangle;
8import java.awt.RenderingHints;
9import java.awt.Shape;
10import java.awt.event.ActionEvent;
11import java.awt.event.ActionListener;
12import java.awt.event.MouseEvent;
13import java.awt.geom.Ellipse2D;
14
15import javax.swing.JComponent;
16import javax.swing.JMenuItem;
17import javax.swing.JPopupMenu;
18import javax.swing.JSlider;
19import javax.swing.SwingUtilities;
20import javax.swing.event.ChangeEvent;
21import javax.swing.event.ChangeListener;
22import javax.swing.plaf.basic.BasicSliderUI;
23
24/*
25The MIT License
26
27Copyright (c) 2010 Ernest Yu. All rights reserved.
28
29Permission is hereby granted, free of charge, to any person obtaining a copy
30of this software and associated documentation files (the "Software"), to deal
31in the Software without restriction, including without limitation the rights
32to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
33copies of the Software, and to permit persons to whom the Software is
34furnished to do so, subject to the following conditions:
35
36The above copyright notice and this permission notice shall be included in
37all copies or substantial portions of the Software.
38
39THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
40IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
41FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
42AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
43LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
44OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
45THE SOFTWARE.
46 */
47/**
48 * UI delegate for the RangeSlider component. RangeSliderUI paints two thumbs,
49 * one for the lower value and one for the upper value.
50 */
51class RangeSliderUI extends BasicSliderUI
52{
53
54    /**
55     * Color of selected range.
56     */
57    private Color rangeColor = Color.GREEN;
58
59    /**
60     * Location and size of thumb for upper value.
61     */
62    private Rectangle upperThumbRect;
63    /**
64     * Indicator that determines whether upper thumb is selected.
65     */
66    private boolean upperThumbSelected;
67
68    /**
69     * Indicator that determines whether lower thumb is being dragged.
70     */
71    private transient boolean lowerDragging;
72    /**
73     * Indicator that determines whether upper thumb is being dragged.
74     */
75    private transient boolean upperDragging;
76
77    /**
78     * Constructs a RangeSliderUI for the specified slider component.
79     *
80     * @param b RangeSlider
81     */
82    public RangeSliderUI(RangeSlider b)
83    {
84        super(b);
85    }
86
87    /**
88     * Installs this UI delegate on the specified component.
89     */
90    @Override
91    public void installUI(JComponent c)
92    {
93        upperThumbRect = new Rectangle();
94        super.installUI(c);
95    }
96
97    /**
98     * Creates a listener to handle track events in the specified slider.
99     */
100    @Override
101    protected TrackListener createTrackListener(JSlider slider)
102    {
103        return new RangeTrackListener();
104    }
105
106    /**
107     * Creates a listener to handle change events in the specified slider.
108     */
109    @Override
110    protected ChangeListener createChangeListener(JSlider slider)
111    {
112        return new ChangeHandler();
113    }
114
115    /**
116     * Updates the dimensions for both thumbs.
117     */
118    @Override
119    protected void calculateThumbSize()
120    {
121        // Call superclass method for lower thumb size.
122        super.calculateThumbSize();
123
124        // Set upper thumb size.
125        upperThumbRect.setSize(thumbRect.width, thumbRect.height);
126    }
127
128    /**
129     * Updates the locations for both thumbs.
130     */
131    @Override
132    protected void calculateThumbLocation()
133    {
134        // Call superclass method for lower thumb location.
135        super.calculateThumbLocation();
136
137        // Adjust upper value to snap to ticks if necessary.
138        if (slider.getSnapToTicks())
139        {
140            int upperValue = slider.getValue() + slider.getExtent();
141            int snappedValue = upperValue;
142            int majorTickSpacing = slider.getMajorTickSpacing();
143            int minorTickSpacing = slider.getMinorTickSpacing();
144            int tickSpacing = 0;
145
146            if (minorTickSpacing > 0)
147            {
148                tickSpacing = minorTickSpacing;
149            }
150            else if (majorTickSpacing > 0)
151            {
152                tickSpacing = majorTickSpacing;
153            }
154
155            if (tickSpacing != 0)
156            {
157                // If it's not on a tick, change the value
158                if ((upperValue - slider.getMinimum()) % tickSpacing != 0)
159                {
160                    float temp = (float) (upperValue - slider.getMinimum()) / (float) tickSpacing;
161                    int whichTick = Math.round(temp);
162                    snappedValue = slider.getMinimum() + (whichTick * tickSpacing);
163                }
164
165                if (snappedValue != upperValue)
166                {
167                    slider.setExtent(snappedValue - slider.getValue());
168                }
169            }
170        }
171
172        // Calculate upper thumb location.  The thumb is centered over its
173        // value on the track.
174        if (slider.getOrientation() == JSlider.HORIZONTAL)
175        {
176            int upperPosition = xPositionForValue(slider.getValue() + slider.getExtent());
177            upperThumbRect.x = upperPosition - (upperThumbRect.width / 2);
178            upperThumbRect.y = trackRect.y;
179
180        }
181        else
182        {
183            int upperPosition = yPositionForValue(slider.getValue() + slider.getExtent());
184            upperThumbRect.x = trackRect.x;
185            upperThumbRect.y = upperPosition - (upperThumbRect.height / 2);
186        }
187    }
188
189    /**
190     * Returns the size of a thumb.
191     */
192    @Override
193    protected Dimension getThumbSize()
194    {
195        return new Dimension(12, 12);
196    }
197
198    /**
199     * Paints the slider. The selected thumb is always painted on top of the
200     * other thumb.
201     */
202    @Override
203    public void paint(Graphics g, JComponent c)
204    {
205        super.paint(g, c);
206
207        Rectangle clipRect = g.getClipBounds();
208        if (upperThumbSelected)
209        {
210            // Paint lower thumb first, then upper thumb.
211            if (clipRect.intersects(thumbRect))
212            {
213                paintLowerThumb(g);
214            }
215            if (clipRect.intersects(upperThumbRect))
216            {
217                paintUpperThumb(g);
218            }
219
220        }
221        else
222        {
223            // Paint upper thumb first, then lower thumb.
224            if (clipRect.intersects(upperThumbRect))
225            {
226                paintUpperThumb(g);
227            }
228            if (clipRect.intersects(thumbRect))
229            {
230                paintLowerThumb(g);
231            }
232        }
233    }
234
235    /**
236     * Paints the track.
237     */
238    @Override
239    public void paintTrack(Graphics g)
240    {
241        // Draw track.
242        super.paintTrack(g);
243
244        Rectangle trackBounds = trackRect;
245
246        if (slider.getOrientation() == JSlider.HORIZONTAL)
247        {
248            // Determine position of selected range by moving from the middle
249            // of one thumb to the other.
250            int lowerX = thumbRect.x + (thumbRect.width / 2);
251            int upperX = upperThumbRect.x + (upperThumbRect.width / 2);
252
253            // Determine track position.
254            int cy = (trackBounds.height / 2) - 2;
255
256            // Save color and shift position.
257            Color oldColor = g.getColor();
258            g.translate(trackBounds.x, trackBounds.y + cy);
259
260            // Draw selected range.
261            g.setColor(rangeColor);
262            for (int y = 0; y <= 3; y++)
263            {
264                g.drawLine(lowerX - trackBounds.x, y, upperX - trackBounds.x, y);
265            }
266
267            // Restore position and color.
268            g.translate(-trackBounds.x, -(trackBounds.y + cy));
269            g.setColor(oldColor);
270
271        }
272        else
273        {
274            // Determine position of selected range by moving from the middle
275            // of one thumb to the other.
276            int lowerY = thumbRect.x + (thumbRect.width / 2);
277            int upperY = upperThumbRect.x + (upperThumbRect.width / 2);
278
279            // Determine track position.
280            int cx = (trackBounds.width / 2) - 2;
281
282            // Save color and shift position.
283            Color oldColor = g.getColor();
284            g.translate(trackBounds.x + cx, trackBounds.y);
285
286            // Draw selected range.
287            g.setColor(rangeColor);
288            for (int x = 0; x <= 3; x++)
289            {
290                g.drawLine(x, lowerY - trackBounds.y, x, upperY - trackBounds.y);
291            }
292
293            // Restore position and color.
294            g.translate(-(trackBounds.x + cx), -trackBounds.y);
295            g.setColor(oldColor);
296        }
297    }
298
299    /**
300     * Overrides superclass method to do nothing. Thumb painting is handled
301     * within the <code>paint()</code> method.
302     */
303    @Override
304    public void paintThumb(Graphics g)
305    {
306        // Do nothing.
307    }
308
309    /**
310     * Paints the thumb for the lower value using the specified graphics object.
311     */
312    private void paintLowerThumb(Graphics g)
313    {
314        Rectangle knobBounds = thumbRect;
315        int w = knobBounds.width;
316        int h = knobBounds.height;
317
318        // Create graphics copy.
319        Graphics2D g2d = (Graphics2D) g.create();
320
321        // Create default thumb shape.
322        Shape thumbShape = createThumbShape(w - 1, h - 1);
323
324        // Draw thumb.
325        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
326                RenderingHints.VALUE_ANTIALIAS_ON);
327        g2d.translate(knobBounds.x, knobBounds.y);
328
329        g2d.setColor(Color.CYAN);
330        g2d.fill(thumbShape);
331
332        g2d.setColor(Color.BLUE);
333        g2d.draw(thumbShape);
334
335        // Dispose graphics.
336        g2d.dispose();
337    }
338
339    /**
340     * Paints the thumb for the upper value using the specified graphics object.
341     */
342    private void paintUpperThumb(Graphics g)
343    {
344        Rectangle knobBounds = upperThumbRect;
345        int w = knobBounds.width;
346        int h = knobBounds.height;
347
348        // Create graphics copy.
349        Graphics2D g2d = (Graphics2D) g.create();
350
351        // Create default thumb shape.
352        Shape thumbShape = createThumbShape(w - 1, h - 1);
353
354        // Draw thumb.
355        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
356                RenderingHints.VALUE_ANTIALIAS_ON);
357        g2d.translate(knobBounds.x, knobBounds.y);
358
359        g2d.setColor(Color.PINK);
360        g2d.fill(thumbShape);
361
362        g2d.setColor(Color.RED);
363        g2d.draw(thumbShape);
364
365        // Dispose graphics.
366        g2d.dispose();
367    }
368
369    /**
370     * Returns a Shape representing a thumb.
371     */
372    private Shape createThumbShape(int width, int height)
373    {
374        // Use circular shape.
375        Ellipse2D shape = new Ellipse2D.Double(0, 0, width, height);
376        return shape;
377    }
378
379    /**
380     * Sets the location of the upper thumb, and repaints the slider. This is
381     * called when the upper thumb is dragged to repaint the slider. The
382     * <code>setThumbLocation()</code> method performs the same task for the
383     * lower thumb.
384     */
385    private void setUpperThumbLocation(int x, int y)
386    {
387        Rectangle upperUnionRect = new Rectangle();
388        upperUnionRect.setBounds(upperThumbRect);
389
390        upperThumbRect.setLocation(x, y);
391
392        SwingUtilities.computeUnion(upperThumbRect.x, upperThumbRect.y, upperThumbRect.width, upperThumbRect.height, upperUnionRect);
393        slider.repaint(upperUnionRect.x, upperUnionRect.y, upperUnionRect.width, upperUnionRect.height);
394    }
395
396    /**
397     * Moves the selected thumb in the specified direction by a block increment.
398     * This method is called when the user presses the Page Up or Down keys.
399     */
400    public void scrollByBlock(int direction)
401    {
402        synchronized (slider)
403        {
404            int blockIncrement = (slider.getMaximum() - slider.getMinimum()) / 10;
405            if (blockIncrement <= 0 && slider.getMaximum() > slider.getMinimum())
406            {
407                blockIncrement = 1;
408            }
409            int delta = blockIncrement * ((direction > 0) ? POSITIVE_SCROLL : NEGATIVE_SCROLL);
410
411            if (upperThumbSelected)
412            {
413                int oldValue = ((RangeSlider) slider).getUpperValue();
414                ((RangeSlider) slider).setUpperValue(oldValue + delta);
415            }
416            else
417            {
418                int oldValue = slider.getValue();
419                slider.setValue(oldValue + delta);
420            }
421        }
422    }
423
424    /**
425     * Moves the selected thumb in the specified direction by a unit increment.
426     * This method is called when the user presses one of the arrow keys.
427     */
428    public void scrollByUnit(int direction)
429    {
430        synchronized (slider)
431        {
432            int delta = 1 * ((direction > 0) ? POSITIVE_SCROLL : NEGATIVE_SCROLL);
433
434            if (upperThumbSelected)
435            {
436                int oldValue = ((RangeSlider) slider).getUpperValue();
437                ((RangeSlider) slider).setUpperValue(oldValue + delta);
438            }
439            else
440            {
441                int oldValue = slider.getValue();
442                slider.setValue(oldValue + delta);
443            }
444        }
445    }
446
447    /**
448     * Listener to handle model change events. This calculates the thumb
449     * locations and repaints the slider if the value change is not caused by
450     * dragging a thumb.
451     */
452    public class ChangeHandler implements ChangeListener
453    {
454
455        public void stateChanged(ChangeEvent arg0)
456        {
457            if (!lowerDragging && !upperDragging)
458            {
459                calculateThumbLocation();
460                slider.repaint();
461            }
462        }
463    }
464
465    /*
466    *   Popup menu for incident actions
467     */
468    private JPopupMenu createPopup()
469    {
470        JPopupMenu menu = new JPopupMenu();
471        JMenuItem eventsMenuItem = new JMenuItem("Events");
472        JMenuItem propsMenuItem = new JMenuItem("Properties");
473        JMenuItem deleteMenuItem = new JMenuItem("Delete");
474        eventsMenuItem.setActionCommand("Edit Events");
475        propsMenuItem.setActionCommand("Modify Incident Properties");
476        deleteMenuItem.setActionCommand("Delete Incident");
477
478        PopupMenuItemListener menuItemListener = new PopupMenuItemListener();
479
480        eventsMenuItem.addActionListener(menuItemListener);
481        propsMenuItem.addActionListener(menuItemListener);
482        deleteMenuItem.addActionListener(menuItemListener);
483
484        menu.add(eventsMenuItem);
485        menu.add(propsMenuItem);
486        menu.add(deleteMenuItem);
487        return menu;
488    }
489
490    class PopupMenuItemListener implements ActionListener
491    {
492
493        public void actionPerformed(ActionEvent e)
494        {
495            System.out.println(e.getActionCommand() + " will be handled here.");
496        }
497    }
498
499    /**
500     * Listener to handle mouse movements in the slider track.
501     */
502    public class RangeTrackListener extends TrackListener
503    {
504
505        @Override
506        public void mousePressed(MouseEvent e)
507        {
508            if (!slider.isEnabled())
509            {
510                return;
511            }
512
513            currentMouseX = e.getX();
514            currentMouseY = e.getY();
515
516            if (slider.isRequestFocusEnabled())
517            {
518                slider.requestFocus();
519            }
520            // Does user want a popup menu?
521            if (e.isPopupTrigger())
522            {
523                JPopupMenu popup = createPopup();
524                popup.show(e.getComponent(), currentMouseX, currentMouseY);
525            }
526            // Determine which thumb is pressed.  If the upper thumb is
527            // selected (last one dragged), then check its position first;
528            // otherwise check the position of the lower thumb first.
529            boolean lowerPressed = false;
530            boolean upperPressed = false;
531            if (upperThumbSelected || slider.getMinimum() == slider.getValue())
532            {
533                if (upperThumbRect.contains(currentMouseX, currentMouseY))
534                {
535                    upperPressed = true;
536                }
537                else if (thumbRect.contains(currentMouseX, currentMouseY))
538                {
539                    lowerPressed = true;
540                }
541            }
542            else
543            {
544                if (thumbRect.contains(currentMouseX, currentMouseY))
545                {
546                    lowerPressed = true;
547                }
548                else if (upperThumbRect.contains(currentMouseX, currentMouseY))
549                {
550                    upperPressed = true;
551                }
552            }
553
554            // Handle lower thumb pressed.
555            if (lowerPressed)
556            {
557                switch (slider.getOrientation())
558                {
559                    case JSlider.VERTICAL:
560                        offset = currentMouseY - thumbRect.y;
561                        break;
562                    case JSlider.HORIZONTAL:
563                        offset = currentMouseX - thumbRect.x;
564                        break;
565                }
566                upperThumbSelected = false;
567                lowerDragging = true;
568                return;
569            }
570            lowerDragging = false;
571
572            // Handle upper thumb pressed.
573            if (upperPressed)
574            {
575                switch (slider.getOrientation())
576                {
577                    case JSlider.VERTICAL:
578                        offset = currentMouseY - upperThumbRect.y;
579                        break;
580                    case JSlider.HORIZONTAL:
581                        offset = currentMouseX - upperThumbRect.x;
582                        break;
583                }
584                upperThumbSelected = true;
585                upperDragging = true;
586                return;
587            }
588            upperDragging = false;
589        }
590
591        @Override
592        public void mouseReleased(MouseEvent e)
593        {
594            lowerDragging = false;
595            upperDragging = false;
596            slider.setValueIsAdjusting(false);
597            super.mouseReleased(e);
598        }
599
600        @Override
601        public void mouseDragged(MouseEvent e)
602        {
603            if (!slider.isEnabled())
604            {
605                return;
606            }
607
608            currentMouseX = e.getX();
609            currentMouseY = e.getY();
610
611            if (lowerDragging)
612            {
613                slider.setValueIsAdjusting(true);
614                moveLowerThumb();
615
616            }
617            else if (upperDragging)
618            {
619                slider.setValueIsAdjusting(true);
620                moveUpperThumb();
621            }
622        }
623
624        @Override
625        public boolean shouldScroll(int direction)
626        {
627            return false;
628        }
629
630        /**
631         * Moves the location of the lower thumb, and sets its corresponding
632         * value in the slider.
633         */
634        private void moveLowerThumb()
635        {
636            int thumbMiddle = 0;
637
638            switch (slider.getOrientation())
639            {
640                case JSlider.VERTICAL:
641                    int halfThumbHeight = thumbRect.height / 2;
642                    int thumbTop = currentMouseY - offset;
643                    int trackTop = trackRect.y;
644                    int trackBottom = trackRect.y + (trackRect.height - 1);
645                    int vMax = yPositionForValue(slider.getValue() + slider.getExtent());
646
647                    // Apply bounds to thumb position.
648                    if (drawInverted())
649                    {
650                        trackBottom = vMax;
651                    }
652                    else
653                    {
654                        trackTop = vMax;
655                    }
656                    thumbTop = Math.max(thumbTop, trackTop - halfThumbHeight);
657                    thumbTop = Math.min(thumbTop, trackBottom - halfThumbHeight);
658
659                    setThumbLocation(thumbRect.x, thumbTop);
660
661                    // Update slider value.
662                    thumbMiddle = thumbTop + halfThumbHeight;
663                    slider.setValue(valueForYPosition(thumbMiddle));
664                    break;
665
666                case JSlider.HORIZONTAL:
667                    int halfThumbWidth = thumbRect.width / 2;
668                    int thumbLeft = currentMouseX - offset;
669                    int trackLeft = trackRect.x;
670                    int trackRight = trackRect.x + (trackRect.width - 1);
671                    int hMax = xPositionForValue(slider.getValue() + slider.getExtent());
672
673                    // Apply bounds to thumb position.
674                    if (drawInverted())
675                    {
676                        trackLeft = hMax;
677                    }
678                    else
679                    {
680                        trackRight = hMax;
681                    }
682                    thumbLeft = Math.max(thumbLeft, trackLeft - halfThumbWidth);
683                    thumbLeft = Math.min(thumbLeft, trackRight - halfThumbWidth);
684
685                    setThumbLocation(thumbLeft, thumbRect.y);
686
687                    // Update slider value.
688                    thumbMiddle = thumbLeft + halfThumbWidth;
689                    slider.setValue(valueForXPosition(thumbMiddle));
690                    break;
691
692                default:
693                    return;
694            }
695        }
696
697        /**
698         * Moves the location of the upper thumb, and sets its corresponding
699         * value in the slider.
700         */
701        private void moveUpperThumb()
702        {
703            int thumbMiddle = 0;
704
705            switch (slider.getOrientation())
706            {
707                case JSlider.VERTICAL:
708                    int halfThumbHeight = thumbRect.height / 2;
709                    int thumbTop = currentMouseY - offset;
710                    int trackTop = trackRect.y;
711                    int trackBottom = trackRect.y + (trackRect.height - 1);
712                    int vMin = yPositionForValue(slider.getValue());
713
714                    // Apply bounds to thumb position.
715                    if (drawInverted())
716                    {
717                        trackTop = vMin;
718                    }
719                    else
720                    {
721                        trackBottom = vMin;
722                    }
723                    thumbTop = Math.max(thumbTop, trackTop - halfThumbHeight);
724                    thumbTop = Math.min(thumbTop, trackBottom - halfThumbHeight);
725
726                    setUpperThumbLocation(thumbRect.x, thumbTop);
727
728                    // Update slider extent.
729                    thumbMiddle = thumbTop + halfThumbHeight;
730                    slider.setExtent(valueForYPosition(thumbMiddle) - slider.getValue());
731                    break;
732
733                case JSlider.HORIZONTAL:
734                    int halfThumbWidth = thumbRect.width / 2;
735                    int thumbLeft = currentMouseX - offset;
736                    int trackLeft = trackRect.x;
737                    int trackRight = trackRect.x + (trackRect.width - 1);
738                    int hMin = xPositionForValue(slider.getValue());
739
740                    // Apply bounds to thumb position.
741                    if (drawInverted())
742                    {
743                        trackRight = hMin;
744                    }
745                    else
746                    {
747                        trackLeft = hMin;
748                    }
749                    thumbLeft = Math.max(thumbLeft, trackLeft - halfThumbWidth);
750                    thumbLeft = Math.min(thumbLeft, trackRight - halfThumbWidth);
751
752                    setUpperThumbLocation(thumbLeft, thumbRect.y);
753
754                    // Update slider extent.
755                    thumbMiddle = thumbLeft + halfThumbWidth;
756                    slider.setExtent(valueForXPosition(thumbMiddle) - slider.getValue());
757                    break;
758
759                default:
760                    return;
761            }
762        }
763    }
764}
Note: See TracBrowser for help on using the repository browser.