2
* Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved.
4
* Redistribution and use in source and binary forms, with or without
5
* modification, are permitted provided that the following conditions are met:
7
* o Redistributions of source code must retain the above copyright notice,
8
* this list of conditions and the following disclaimer.
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.
14
* o Neither the name of Flamingo 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.
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.
30
package org.pushingpixels.flamingo.internal.ui.common;
33
import java.awt.event.*;
34
import java.beans.PropertyChangeEvent;
35
import java.beans.PropertyChangeListener;
38
import javax.swing.plaf.ComponentUI;
40
import org.pushingpixels.flamingo.api.common.JCommandButton;
41
import org.pushingpixels.flamingo.api.common.JScrollablePanel;
42
import org.pushingpixels.flamingo.api.common.JScrollablePanel.ScrollType;
43
import org.pushingpixels.flamingo.internal.utils.DoubleArrowResizableIcon;
46
* Basic UI for scrollable panel {@link JScrollablePanel}.
48
* @author Kirill Grouchnikov
50
public class BasicScrollablePanelUI extends ScrollablePanelUI {
52
* The associated scrollable panel.
54
protected JScrollablePanel scrollablePanel;
56
private JPanel viewport;
58
private JCommandButton leadingScroller;
60
private JCommandButton trailingScroller;
62
private int viewOffset;
64
private MouseWheelListener mouseWheelListener;
66
private PropertyChangeListener propertyChangeListener;
68
private ComponentListener componentListener;
73
* @see javax.swing.plaf.ComponentUI#createUI(javax.swing.JComponent)
75
public static ComponentUI createUI(JComponent c) {
76
return new BasicScrollablePanelUI();
82
* @see javax.swing.plaf.ComponentUI#installUI(javax.swing.JComponent)
85
public void installUI(JComponent c) {
86
this.scrollablePanel = (JScrollablePanel) c;
87
super.installUI(this.scrollablePanel);
93
protected void installListeners() {
94
this.mouseWheelListener = new MouseWheelListener() {
96
public void mouseWheelMoved(MouseWheelEvent e) {
97
if (scrollablePanel.getScrollType() != JScrollablePanel.ScrollType.VERTICALLY) {
101
int scrollAmount = 8 * e.getScrollAmount()
102
* e.getWheelRotation();
103
viewOffset += scrollAmount;
107
this.scrollablePanel.addMouseWheelListener(this.mouseWheelListener);
109
this.propertyChangeListener = new PropertyChangeListener() {
111
public void propertyChange(PropertyChangeEvent evt) {
112
if ("scrollOnRollover".equals(evt.getPropertyName())) {
113
boolean isScrollOnRollover = (Boolean) evt.getNewValue();
114
leadingScroller.setFireActionOnRollover(isScrollOnRollover);
116
.setFireActionOnRollover(isScrollOnRollover);
121
.addPropertyChangeListener(this.propertyChangeListener);
123
if (this.scrollablePanel.getView() != null) {
124
this.componentListener = new ComponentAdapter() {
126
public void componentResized(ComponentEvent e) {
127
scrollablePanel.doLayout();
130
this.scrollablePanel.getView().addComponentListener(
131
this.componentListener);
136
protected void installComponents() {
137
this.viewport = new JPanel(new LayoutManager() {
139
public void addLayoutComponent(String name, Component comp) {
143
public void removeLayoutComponent(Component comp) {
147
public Dimension preferredLayoutSize(Container parent) {
148
return new Dimension(10, 10);
152
public Dimension minimumLayoutSize(Container parent) {
153
return preferredLayoutSize(parent);
157
public void layoutContainer(Container parent) {
158
JComponent view = scrollablePanel.getView();
159
if (scrollablePanel.getScrollType() == ScrollType.HORIZONTALLY) {
160
int viewWidth = view.getPreferredSize().width;
161
int availWidth = parent.getWidth();
163
int offsetX = -viewOffset;
164
view.setBounds(offsetX, 0, Math.max(viewWidth, availWidth),
167
int viewHeight = view.getPreferredSize().height;
168
int availHeight = parent.getHeight();
170
int offsetY = -viewOffset;
171
view.setBounds(0, offsetY, parent.getWidth(), Math.max(
172
viewHeight, availHeight));
176
JComponent view = scrollablePanel.getView();
178
this.viewport.add(view);
180
this.scrollablePanel.add(this.viewport);
182
this.leadingScroller = this.createLeadingScroller();
183
this.configureLeftScrollerButtonAction();
184
this.scrollablePanel.add(this.leadingScroller);
186
this.trailingScroller = this.createTrailingScroller();
187
this.configureRightScrollerButtonAction();
188
this.scrollablePanel.add(this.trailingScroller);
191
protected void installDefaults() {
192
this.scrollablePanel.setLayout(new ScrollablePanelLayout());
198
* @see javax.swing.plaf.ComponentUI#uninstallUI(javax.swing.JComponent)
201
public void uninstallUI(JComponent c) {
202
uninstallListeners();
203
uninstallComponents();
205
super.uninstallUI(this.scrollablePanel);
208
protected void uninstallDefaults() {
211
protected void uninstallComponents() {
212
this.scrollablePanel.remove(this.viewport);
213
this.scrollablePanel.remove(this.leadingScroller);
214
this.scrollablePanel.remove(this.trailingScroller);
217
protected void uninstallListeners() {
219
.removePropertyChangeListener(this.propertyChangeListener);
220
this.propertyChangeListener = null;
222
this.scrollablePanel.removeMouseWheelListener(this.mouseWheelListener);
223
this.mouseWheelListener = null;
225
if (this.scrollablePanel.getView() != null) {
226
this.scrollablePanel.getView().removeComponentListener(
227
this.componentListener);
228
this.componentListener = null;
232
protected JCommandButton createLeadingScroller() {
233
JCommandButton b = new JCommandButton(
235
new DoubleArrowResizableIcon(
237
this.scrollablePanel.getScrollType() == ScrollType.HORIZONTALLY ? SwingConstants.WEST
238
: SwingConstants.NORTH));
240
b.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
241
b.setFocusable(false);
242
b.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
243
b.putClientProperty(BasicCommandButtonUI.EMULATE_SQUARE_BUTTON,
245
b.putClientProperty(BasicCommandButtonUI.DONT_DISPOSE_POPUPS,
250
protected JCommandButton createTrailingScroller() {
251
JCommandButton b = new JCommandButton(
253
new DoubleArrowResizableIcon(
255
this.scrollablePanel.getScrollType() == ScrollType.HORIZONTALLY ? SwingConstants.EAST
256
: SwingConstants.SOUTH));
258
b.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
259
b.setFocusable(false);
260
b.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
261
b.putClientProperty(BasicCommandButtonUI.EMULATE_SQUARE_BUTTON,
263
b.putClientProperty(BasicCommandButtonUI.DONT_DISPOSE_POPUPS,
268
private void syncScrolling() {
269
this.scrollablePanel.doLayout();
272
public void removeScrollers() {
273
if (this.leadingScroller.getParent() == this.scrollablePanel) {
274
this.scrollablePanel.remove(this.leadingScroller);
275
this.scrollablePanel.remove(this.trailingScroller);
277
this.scrollablePanel.revalidate();
278
this.scrollablePanel.repaint();
282
private void addScrollers() {
283
this.scrollablePanel.add(this.leadingScroller);
284
this.scrollablePanel.add(this.trailingScroller);
285
this.scrollablePanel.revalidate();
286
JComponent view = this.scrollablePanel.getView();
287
view.setPreferredSize(view.getMinimumSize());
288
view.setSize(view.getMinimumSize());
289
this.scrollablePanel.doLayout();
291
this.scrollablePanel.repaint();
294
protected void configureLeftScrollerButtonAction() {
295
this.leadingScroller.setAutoRepeatAction(true);
296
this.leadingScroller.setAutoRepeatActionIntervals(200, 50);
297
this.leadingScroller.setFireActionOnRollover(this.scrollablePanel
298
.isScrollOnRollover());
299
this.leadingScroller.addActionListener(new ActionListener() {
301
public void actionPerformed(ActionEvent e) {
308
protected void configureRightScrollerButtonAction() {
309
this.trailingScroller.setAutoRepeatAction(true);
310
this.trailingScroller.setAutoRepeatActionIntervals(200, 50);
311
this.trailingScroller.setFireActionOnRollover(this.scrollablePanel
312
.isScrollOnRollover());
313
this.trailingScroller.addActionListener(new ActionListener() {
315
public void actionPerformed(ActionEvent e) {
323
public void scrollToIfNecessary(int startPosition, int span) {
324
if (this.scrollablePanel.getScrollType() == ScrollType.HORIZONTALLY) {
325
if (this.scrollablePanel.getComponentOrientation().isLeftToRight()) {
326
revealRightEdge(startPosition, span);
327
revealLeftEdge(startPosition);
329
revealLeftEdge(startPosition);
330
revealRightEdge(startPosition, span);
333
revealBottomEdge(startPosition, span);
334
revealTopEdge(startPosition);
338
private void revealLeftEdge(int x) {
339
if (x < viewOffset) {
340
// left edge is not visible
346
private void revealRightEdge(int x, int width) {
347
if ((x + width) > (viewOffset + viewport.getWidth())) {
348
// right edge is not visible
349
viewOffset = x + width - viewport.getWidth() + 5;
354
private void revealTopEdge(int y) {
355
if (y < viewOffset) {
356
// top edge is not visible
362
private void revealBottomEdge(int y, int height) {
363
if ((y + height) > (viewOffset + viewport.getHeight())) {
364
// bottom edge is not visible
365
viewOffset = y + height - viewport.getHeight() + 5;
371
public boolean isShowingScrollButtons() {
372
return (this.leadingScroller.isVisible());
376
* Layout for the scrollable panel.
378
* @author Kirill Grouchnikov
381
protected class ScrollablePanelLayout implements LayoutManager {
383
* Creates new layout manager.
385
public ScrollablePanelLayout() {
391
* @see java.awt.LayoutManager#addLayoutComponent(java.lang.String,
392
* java.awt.Component)
395
public void addLayoutComponent(String name, Component c) {
401
* @see java.awt.LayoutManager#removeLayoutComponent(java.awt.Component)
404
public void removeLayoutComponent(Component c) {
410
* @see java.awt.LayoutManager#preferredLayoutSize(java.awt.Container)
413
public Dimension preferredLayoutSize(Container c) {
414
if (scrollablePanel.getScrollType() == ScrollType.HORIZONTALLY) {
415
return new Dimension(c.getWidth(), 21);
417
return new Dimension(21, c.getHeight());
424
* @see java.awt.LayoutManager#minimumLayoutSize(java.awt.Container)
427
public Dimension minimumLayoutSize(Container c) {
428
if (scrollablePanel.getScrollType() == ScrollType.HORIZONTALLY) {
429
return new Dimension(10, 21);
431
return new Dimension(21, 10);
438
* @see java.awt.LayoutManager#layoutContainer(java.awt.Container)
441
public void layoutContainer(Container c) {
442
int width = c.getWidth();
443
int height = c.getHeight();
445
Insets ins = c.getInsets();
447
JComponent view = scrollablePanel.getView();
448
Dimension viewPrefSize = view.getPreferredSize();
450
// System.out.println(width + "*" + height + " - "
451
// + viewPrefSize.width + "*" + viewPrefSize.height);
453
if (scrollablePanel.getScrollType() == ScrollType.HORIZONTALLY) {
454
boolean shouldShowScrollerButtons = (viewPrefSize.width > width);
456
leadingScroller.setVisible(shouldShowScrollerButtons);
457
trailingScroller.setVisible(shouldShowScrollerButtons);
459
int scrollPanelWidth = shouldShowScrollerButtons ? width
460
- ins.left - ins.right
461
- leadingScroller.getPreferredSize().width
462
- trailingScroller.getPreferredSize().width - 4 : width
463
- ins.left - ins.right;
465
if (shouldShowScrollerButtons) {
466
int spw = leadingScroller.getPreferredSize().width;
467
leadingScroller.setBounds(x, ins.top, spw, height - ins.top
471
viewport.setBounds(x, ins.top, scrollPanelWidth, height
472
- ins.top - ins.bottom);
474
int viewPreferredWidth = view.getPreferredSize().width;
475
if (viewOffset < 0) {
478
if ((viewPreferredWidth > 0)
479
&& (viewOffset + scrollPanelWidth > viewPreferredWidth)) {
480
viewOffset = Math.max(0, viewPreferredWidth
485
x += scrollPanelWidth + 2;
486
if (shouldShowScrollerButtons) {
487
int spw = trailingScroller.getPreferredSize().width;
488
trailingScroller.setBounds(x, ins.top, spw, height
489
- ins.top - ins.bottom);
492
boolean shouldShowScrollerButtons = (viewPrefSize.height > height);
494
leadingScroller.setVisible(shouldShowScrollerButtons);
495
trailingScroller.setVisible(shouldShowScrollerButtons);
497
int scrollPanelHeight = shouldShowScrollerButtons ? height
498
- ins.top - ins.bottom
499
- leadingScroller.getPreferredSize().height
500
- trailingScroller.getPreferredSize().height - 4
501
: height - ins.top - ins.bottom;
503
if (shouldShowScrollerButtons) {
504
int sph = leadingScroller.getPreferredSize().height;
505
leadingScroller.setBounds(ins.left, y, width - ins.left
509
viewport.setBounds(ins.left, y, width - ins.left - ins.right,
512
int viewPreferredHeight = view.getPreferredSize().height;
513
if (viewOffset < 0) {
516
if ((viewPreferredHeight > 0)
517
&& (viewOffset + scrollPanelHeight > viewPreferredHeight)) {
518
viewOffset = Math.max(0, viewPreferredHeight
519
- scrollPanelHeight);
523
y += scrollPanelHeight + 2;
524
if (shouldShowScrollerButtons) {
525
int sph = trailingScroller.getPreferredSize().height;
526
trailingScroller.setBounds(ins.left, y, width - ins.left
531
if (scrollablePanel.getScrollType() == ScrollType.HORIZONTALLY) {
533
.setEnabled((viewOffset + viewport.getWidth()) < view
537
.setEnabled((viewOffset + viewport.getHeight()) < view
540
leadingScroller.setEnabled(viewOffset > 0);