~ubuntu-branches/debian/stretch/insubstantial/stretch

« back to all changes in this revision

Viewing changes to laf-widget/src/main/java/org/pushingpixels/lafwidget/scroll/ScrollPaneSelector.java

  • Committer: Package Import Robot
  • Author(s): Felix Natter
  • Date: 2016-01-18 20:58:45 UTC
  • Revision ID: package-import@ubuntu.com-20160118205845-crbmrkda61qsi5qa
Tags: upstream-7.3+dfsg2
ImportĀ upstreamĀ versionĀ 7.3+dfsg2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (c) 2005-2010 Laf-Widget Kirill Grouchnikov. All Rights Reserved.
 
3
 *
 
4
 * Redistribution and use in source and binary forms, with or without
 
5
 * modification, are permitted provided that the following conditions are met:
 
6
 *
 
7
 *  o Redistributions of source code must retain the above copyright notice,
 
8
 *    this list of conditions and the following disclaimer.
 
9
 *
 
10
 *  o Redistributions in binary form must reproduce the above copyright notice,
 
11
 *    this list of conditions and the following disclaimer in the documentation
 
12
 *    and/or other materials provided with the distribution.
 
13
 *
 
14
 *  o Neither the name of Laf-Widget Kirill Grouchnikov nor the names of
 
15
 *    its contributors may be used to endorse or promote products derived
 
16
 *    from this software without specific prior written permission.
 
17
 *
 
18
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 
19
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 
20
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 
21
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 
22
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 
23
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 
24
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 
25
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 
26
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 
27
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 
28
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
29
 */
 
30
package org.pushingpixels.lafwidget.scroll;
 
31
 
 
32
import java.awt.*;
 
33
import java.awt.event.*;
 
34
import java.awt.geom.Area;
 
35
import java.awt.image.BufferedImage;
 
36
import java.beans.PropertyChangeEvent;
 
37
import java.beans.PropertyChangeListener;
 
38
 
 
39
import javax.swing.*;
 
40
import javax.swing.event.MouseInputAdapter;
 
41
import javax.swing.event.MouseInputListener;
 
42
 
 
43
import org.pushingpixels.lafwidget.LafWidgetRepository;
 
44
import org.pushingpixels.lafwidget.LafWidgetUtilities2;
 
45
import org.pushingpixels.lafwidget.animation.AnimationConfigurationManager;
 
46
import org.pushingpixels.lafwidget.preview.PreviewPainter;
 
47
import org.pushingpixels.trident.Timeline;
 
48
import org.pushingpixels.trident.Timeline.TimelineState;
 
49
import org.pushingpixels.trident.callback.UIThreadTimelineCallbackAdapter;
 
50
 
 
51
/**
 
52
 * ScrollPaneSelector is a little utility class that provides a means to quickly
 
53
 * scroll both vertically and horizontally on a single mouse click, by dragging
 
54
 * a selection rectangle over a "thumbnail" of the scrollPane's viewport view.
 
55
 * <p>
 
56
 * Once the selector is installed on a given JScrollPane instance, a little
 
57
 * button appears as soon as at least one of its scroll bars is made visible.
 
58
 * <p>
 
59
 * Contributed by the original author under BSD license. Also appears in the <a
 
60
 * href="https://jdnc-incubator.dev.java.net">JDNC Incubator</a>.
 
61
 * 
 
62
 * @author weebib (Pierre LE LANNIC)
 
63
 * @author Kirill Grouchnikov (animations).
 
64
 */
 
65
public class ScrollPaneSelector extends JComponent {
 
66
        // static final fields
 
67
        private static final double MAX_SIZE = 200;
 
68
        // private static final Icon LAUNCH_SELECTOR_ICON = new Icon() {
 
69
        // public void paintIcon(Component c, Graphics g, int x, int y) {
 
70
        // Color tmpColor = g.getColor();
 
71
        // g.setColor(Color.BLACK);
 
72
        // g.drawRect(2, 2, 10, 10);
 
73
        // g.drawRect(4, 5, 6, 4);
 
74
        // g.setColor(tmpColor);
 
75
        // }
 
76
        //
 
77
        // public int getIconWidth() {
 
78
        // return 15;
 
79
        // }
 
80
        //
 
81
        // public int getIconHeight() {
 
82
        // return 15;
 
83
        // }
 
84
        // };
 
85
        // private static Map theInstalledScrollPaneSelectors = new HashMap();
 
86
        private static final String COMPONENT_ORIENTATION = "componentOrientation";
 
87
 
 
88
        // private static final String HAS_BEEN_UNINSTALLED =
 
89
        // "lafwidget.internal.scrollPaneSelector.hasBeenUninstalled";
 
90
 
 
91
        // member fields
 
92
        private LayoutManager theFormerLayoutManager;
 
93
        private JScrollPane theScrollPane;
 
94
        private JComponent theComponent;
 
95
        private JPopupMenu thePopupMenu;
 
96
        private boolean toRestoreOriginal;
 
97
        private JButton theButton;
 
98
        private BufferedImage theImage;
 
99
        private Rectangle theStartRectangle;
 
100
        private Rectangle theRectangle;
 
101
        private Point theStartPoint;
 
102
        private Point thePrevPoint;
 
103
        private double theScale;
 
104
        private PropertyChangeListener propertyChangeListener;
 
105
        private ContainerAdapter theViewPortViewListener;
 
106
 
 
107
        // -- Constructor ------
 
108
        ScrollPaneSelector() {
 
109
                setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
 
110
                theScrollPane = null;
 
111
                theImage = null;
 
112
                theStartRectangle = null;
 
113
                theRectangle = null;
 
114
                theStartPoint = null;
 
115
                theScale = 0.0;
 
116
                theButton = new JButton();
 
117
                LafWidgetRepository.getRepository().getLafSupport().markButtonAsFlat(
 
118
                                theButton);
 
119
                theButton.setFocusable(false);
 
120
                theButton.setFocusPainted(false);
 
121
 
 
122
                MouseInputListener mil = new MouseInputAdapter() {
 
123
                        @Override
 
124
                        public void mousePressed(MouseEvent e) {
 
125
                                Point p = e.getPoint();
 
126
                                SwingUtilities.convertPointToScreen(p, theButton);
 
127
                                display(p);
 
128
                        }
 
129
 
 
130
                        @Override
 
131
                        public void mouseReleased(MouseEvent e) {
 
132
                                if (!thePopupMenu.isVisible())
 
133
                                        return;
 
134
                                toRestoreOriginal = false;
 
135
                                thePopupMenu.setVisible(false);
 
136
                                theStartRectangle = theRectangle;
 
137
                        }
 
138
 
 
139
                        @Override
 
140
                        public void mouseDragged(MouseEvent e) {
 
141
                                if (theStartPoint == null)
 
142
                                        return;
 
143
 
 
144
                                if (!thePopupMenu.isShowing())
 
145
                                        return;
 
146
 
 
147
                                Point newPoint = e.getPoint();
 
148
                                SwingUtilities.convertPointToScreen(newPoint, (Component) e
 
149
                                                .getSource());
 
150
 
 
151
                                Rectangle popupScreenRect = new Rectangle(thePopupMenu
 
152
                                                .getLocationOnScreen(), thePopupMenu.getSize());
 
153
                                if (!popupScreenRect.contains(newPoint))
 
154
                                        return;
 
155
 
 
156
                                int deltaX = (int) ((newPoint.x - thePrevPoint.x) / theScale);
 
157
                                int deltaY = (int) ((newPoint.y - thePrevPoint.y) / theScale);
 
158
                                scroll(deltaX, deltaY, false);
 
159
 
 
160
                                thePrevPoint = newPoint;
 
161
                        }
 
162
                };
 
163
                theButton.addMouseListener(mil);
 
164
                theButton.addMouseMotionListener(mil);
 
165
                setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
 
166
                thePopupMenu = new JPopupMenu();
 
167
                thePopupMenu.setLayout(new BorderLayout());
 
168
                thePopupMenu.add(this, BorderLayout.CENTER);
 
169
                propertyChangeListener = new PropertyChangeListener() {
 
170
                        @Override
 
171
            public void propertyChange(PropertyChangeEvent evt) {
 
172
                                if (theScrollPane == null)
 
173
                                        return;
 
174
                                if ("componentOrientation".equals(evt.getPropertyName())) {
 
175
                                        theScrollPane.setCorner(JScrollPane.LOWER_LEADING_CORNER,
 
176
                                                        null);
 
177
                                        theScrollPane.setCorner(JScrollPane.LOWER_TRAILING_CORNER,
 
178
                                                        theButton);
 
179
                                }
 
180
                        }
 
181
                };
 
182
                theViewPortViewListener = new ContainerAdapter() {
 
183
                        @Override
 
184
                        public void componentAdded(ContainerEvent e) {
 
185
                                if (thePopupMenu.isVisible())
 
186
                                        thePopupMenu.setVisible(false);
 
187
                                Component comp = theScrollPane.getViewport().getView();
 
188
                                theComponent = (comp instanceof JComponent) ? (JComponent) comp
 
189
                                                : null;
 
190
                        }
 
191
                };
 
192
                thePopupMenu.addPropertyChangeListener(new PropertyChangeListener() {
 
193
                        @Override
 
194
            public void propertyChange(PropertyChangeEvent evt) {
 
195
                                if ("visible".equals(evt.getPropertyName())) {
 
196
                                        if (!thePopupMenu.isVisible()) {
 
197
                                                setCursor(Cursor
 
198
                                                                .getPredefinedCursor(Cursor.DEFAULT_CURSOR));
 
199
                                                if (toRestoreOriginal) {
 
200
                                                        int deltaX = (int) ((thePrevPoint.x - theStartPoint.x) / theScale);
 
201
                                                        int deltaY = (int) ((thePrevPoint.y - theStartPoint.y) / theScale);
 
202
                                                        scroll(-deltaX, -deltaY, true);
 
203
                                                }
 
204
                                        }
 
205
                                }
 
206
                        }
 
207
                });
 
208
        }
 
209
 
 
210
        // -- JComponent overriden methods ------
 
211
        @Override
 
212
        public Dimension getPreferredSize() {
 
213
                if (theImage == null || theRectangle == null)
 
214
                        return new Dimension();
 
215
                Insets insets = getInsets();
 
216
                return new Dimension(theImage.getWidth(null) + insets.left
 
217
                                + insets.right, theImage.getHeight(null) + insets.top
 
218
                                + insets.bottom);
 
219
        }
 
220
 
 
221
        @Override
 
222
        protected void paintComponent(Graphics g1D) {
 
223
                if (theImage == null || theRectangle == null)
 
224
                        return;
 
225
                Graphics2D g = (Graphics2D) g1D.create();
 
226
 
 
227
                Insets insets = getInsets();
 
228
                int xOffset = insets.left;
 
229
                int yOffset = insets.top;
 
230
                int availableWidth = getWidth() - insets.left - insets.right;
 
231
                int availableHeight = getHeight() - insets.top - insets.bottom;
 
232
                g.drawImage(theImage, xOffset, yOffset, null);
 
233
 
 
234
                Color tmpColor = g.getColor();
 
235
                Area area = new Area(new Rectangle(xOffset, yOffset, availableWidth,
 
236
                                availableHeight));
 
237
                area.subtract(new Area(theRectangle));
 
238
                g.setColor(new Color(200, 200, 200, 128));
 
239
                g.fill(area);
 
240
                g.setColor(Color.BLACK);
 
241
                g.draw(theRectangle);
 
242
                g.setColor(tmpColor);
 
243
 
 
244
                g.dispose();
 
245
        }
 
246
 
 
247
        // -- Private methods ------
 
248
        void installOnScrollPane(JScrollPane aScrollPane) {
 
249
                if (theScrollPane != null)
 
250
                        uninstallFromScrollPane();
 
251
                theScrollPane = aScrollPane;
 
252
                theFormerLayoutManager = theScrollPane.getLayout();
 
253
                theScrollPane.setLayout(new TweakedScrollPaneLayout());
 
254
                theScrollPane.firePropertyChange("layoutManager", false, true);
 
255
                theScrollPane.addPropertyChangeListener(COMPONENT_ORIENTATION,
 
256
                                propertyChangeListener);
 
257
                theScrollPane.getViewport().addContainerListener(
 
258
                                theViewPortViewListener);
 
259
                theScrollPane.setCorner(JScrollPane.LOWER_TRAILING_CORNER, theButton);
 
260
                Component comp = theScrollPane.getViewport().getView();
 
261
                theComponent = (comp instanceof JComponent) ? (JComponent) comp : null;
 
262
 
 
263
                this.theButton.setIcon(LafWidgetRepository.getRepository()
 
264
                                .getLafSupport().getSearchIcon(
 
265
                                                UIManager.getInt("ScrollBar.width") - 3,
 
266
                                                theScrollPane.getComponentOrientation()));
 
267
 
 
268
                theScrollPane.doLayout();
 
269
        }
 
270
 
 
271
        void uninstallFromScrollPane() {
 
272
                if (theScrollPane == null)
 
273
                        return;
 
274
                if (thePopupMenu.isVisible())
 
275
                        thePopupMenu.setVisible(false);
 
276
                theScrollPane.setCorner(JScrollPane.LOWER_TRAILING_CORNER, null);
 
277
                theScrollPane.removePropertyChangeListener(COMPONENT_ORIENTATION,
 
278
                                propertyChangeListener);
 
279
                theScrollPane.getViewport().removeContainerListener(
 
280
                                theViewPortViewListener);
 
281
                theScrollPane.setLayout(theFormerLayoutManager);
 
282
                theScrollPane.firePropertyChange("layoutManager", true, false);
 
283
                theScrollPane = null;
 
284
        }
 
285
 
 
286
        private void display(Point aPointOnScreen) {
 
287
                if (theComponent == null)
 
288
                        return;
 
289
 
 
290
                PreviewPainter previewPainter = LafWidgetUtilities2
 
291
                                .getComponentPreviewPainter(theScrollPane);
 
292
                if (!previewPainter.hasPreview(theComponent.getParent(), theComponent,
 
293
                                0))
 
294
                        return;
 
295
 
 
296
                // if (previewPainter == null) {
 
297
                // double compWidth = theComponent.getWidth();
 
298
                // double compHeight = theComponent.getHeight();
 
299
                // double scaleX = MAX_SIZE / compWidth;
 
300
                // double scaleY = MAX_SIZE / compHeight;
 
301
                // theScale = Math.min(scaleX, scaleY);
 
302
                // theImage = new BufferedImage(
 
303
                // (int) (theComponent.getWidth() * theScale),
 
304
                // (int) (theComponent.getHeight() * theScale),
 
305
                // BufferedImage.TYPE_INT_RGB);
 
306
                //
 
307
                // Graphics2D g = theImage.createGraphics();
 
308
                // g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
 
309
                // RenderingHints.VALUE_INTERPOLATION_BILINEAR);
 
310
                // g.scale(theScale, theScale);
 
311
                // theComponent.paint(g);
 
312
                // g.dispose();
 
313
                // } else {
 
314
                Dimension pDimension = previewPainter.getPreviewWindowDimension(
 
315
                                theComponent.getParent(), theComponent, 0);
 
316
                double compWidth = theComponent.getWidth();
 
317
                double compHeight = theComponent.getHeight();
 
318
                double scaleX = pDimension.getWidth() / compWidth;
 
319
                double scaleY = pDimension.getHeight() / compHeight;
 
320
                theScale = Math.min(scaleX, scaleY);
 
321
                theImage = new BufferedImage(
 
322
                                (int) (theComponent.getWidth() * theScale), (int) (theComponent
 
323
                                                .getHeight() * theScale), BufferedImage.TYPE_INT_RGB);
 
324
 
 
325
                Graphics2D g = theImage.createGraphics();
 
326
                previewPainter.previewComponent(null, theComponent, 0, g, 0, 0,
 
327
                                theImage.getWidth(), theImage.getHeight());
 
328
                g.dispose();
 
329
                // }
 
330
 
 
331
                theStartRectangle = theComponent.getVisibleRect();
 
332
                Insets insets = getInsets();
 
333
                theStartRectangle.x = (int) (theScale * theStartRectangle.x + insets.left);
 
334
                theStartRectangle.y = (int) (theScale * theStartRectangle.y + insets.right);
 
335
                theStartRectangle.width *= theScale;
 
336
                theStartRectangle.height *= theScale;
 
337
                theRectangle = theStartRectangle;
 
338
 
 
339
                Dimension pref = thePopupMenu.getPreferredSize();
 
340
                Point buttonLocation = theButton.getLocationOnScreen();
 
341
                Point popupLocation = new Point(
 
342
                                (theButton.getWidth() - pref.width) / 2,
 
343
                                (theButton.getHeight() - pref.height) / 2);
 
344
                Point centerPoint = new Point(buttonLocation.x + popupLocation.x
 
345
                                + theRectangle.x + theRectangle.width / 2, buttonLocation.y
 
346
                                + popupLocation.y + theRectangle.y + theRectangle.height / 2);
 
347
                try {
 
348
                        // Attempt to move the mouse pointer to the center of the selector's
 
349
                        // rectangle.
 
350
                        new Robot().mouseMove(centerPoint.x, centerPoint.y);
 
351
                        theStartPoint = centerPoint;
 
352
                } catch (Exception e) {
 
353
                        // Since we cannot move the cursor, we'll move the popup instead.
 
354
                        theStartPoint = aPointOnScreen;
 
355
                        popupLocation.x += theStartPoint.x - centerPoint.x;
 
356
                        popupLocation.y += theStartPoint.y - centerPoint.y;
 
357
                }
 
358
                thePrevPoint = new Point(theStartPoint);
 
359
                toRestoreOriginal = true;
 
360
                thePopupMenu.show(theButton, popupLocation.x, popupLocation.y);
 
361
        }
 
362
 
 
363
        private void moveRectangle(int aDeltaX, int aDeltaY) {
 
364
                if (theStartRectangle == null)
 
365
                        return;
 
366
 
 
367
                Insets insets = getInsets();
 
368
                Rectangle newRect = new Rectangle(theStartRectangle);
 
369
                newRect.x += aDeltaX;
 
370
                newRect.y += aDeltaY;
 
371
                newRect.x = Math.min(Math.max(newRect.x, insets.left), getWidth()
 
372
                                - insets.right - newRect.width);
 
373
                newRect.y = Math.min(Math.max(newRect.y, insets.right), getHeight()
 
374
                                - insets.bottom - newRect.height);
 
375
                Rectangle clip = new Rectangle();
 
376
                Rectangle.union(theRectangle, newRect, clip);
 
377
                clip.grow(2, 2);
 
378
                theRectangle = newRect;
 
379
                paintImmediately(clip);
 
380
        }
 
381
 
 
382
        private void syncRectangle() {
 
383
                JViewport viewport = this.theScrollPane.getViewport();
 
384
                Rectangle viewRect = viewport.getViewRect();
 
385
 
 
386
                Insets insets = getInsets();
 
387
                Rectangle newRect = new Rectangle();
 
388
                newRect.x = (int) (theScale * viewRect.x + insets.left);
 
389
                newRect.y = (int) (theScale * viewRect.y + insets.top);
 
390
                newRect.width = (int) (viewRect.width * theScale);
 
391
                newRect.height = (int) (viewRect.height * theScale);
 
392
 
 
393
                Rectangle clip = new Rectangle();
 
394
                Rectangle.union(theRectangle, newRect, clip);
 
395
                clip.grow(2, 2);
 
396
                theRectangle = newRect;
 
397
 
 
398
                // System.out.println(viewRect + "-->" + theRectangle);
 
399
                paintImmediately(clip);
 
400
        }
 
401
 
 
402
        private void scroll(final int aDeltaX, final int aDeltaY, boolean toAnimate) {
 
403
                if (theComponent == null)
 
404
                        return;
 
405
                final Rectangle oldRectangle = theComponent.getVisibleRect();
 
406
                final Rectangle newRectangle = new Rectangle(oldRectangle.x + aDeltaX,
 
407
                                oldRectangle.y + aDeltaY, oldRectangle.width,
 
408
                                oldRectangle.height);
 
409
 
 
410
                // Animate scrolling
 
411
                if (toAnimate) {
 
412
                        Timeline scrollTimeline = new Timeline(theComponent);
 
413
                        AnimationConfigurationManager.getInstance().configureTimeline(
 
414
                                        scrollTimeline);
 
415
                        scrollTimeline.addCallback(new UIThreadTimelineCallbackAdapter() {
 
416
                                @Override
 
417
                                public void onTimelineStateChanged(TimelineState oldState,
 
418
                                                TimelineState newState, float durationFraction,
 
419
                                                float timelinePosition) {
 
420
                                        if ((oldState == TimelineState.DONE)
 
421
                                                        && (newState == TimelineState.IDLE)) {
 
422
                                                theComponent.scrollRectToVisible(newRectangle);
 
423
                                                syncRectangle();
 
424
                                        }
 
425
                                }
 
426
 
 
427
                                @Override
 
428
                                public void onTimelinePulse(float durationFraction,
 
429
                                                float timelinePosition) {
 
430
                                        int x = (int) (oldRectangle.x + timelinePosition * aDeltaX);
 
431
                                        int y = (int) (oldRectangle.y + timelinePosition * aDeltaY);
 
432
                                        theComponent.scrollRectToVisible(new Rectangle(x, y,
 
433
                                                        oldRectangle.width, oldRectangle.height));
 
434
                                        syncRectangle();
 
435
                                }
 
436
                        });
 
437
                        scrollTimeline.play();
 
438
                } else {
 
439
                        theComponent.scrollRectToVisible(newRectangle);
 
440
                        syncRectangle();
 
441
                }
 
442
        }
 
443
}
 
 
b'\\ No newline at end of file'