1 | package com.jsql.view.swing.scrollpane; | |
2 | ||
3 | import com.jsql.util.LogLevelUtil; | |
4 | import org.apache.logging.log4j.LogManager; | |
5 | import org.apache.logging.log4j.Logger; | |
6 | ||
7 | import javax.swing.*; | |
8 | import javax.swing.plaf.ScrollBarUI; | |
9 | import javax.swing.plaf.basic.BasicScrollBarUI; | |
10 | import java.awt.*; | |
11 | import java.awt.event.ComponentAdapter; | |
12 | import java.awt.event.ComponentEvent; | |
13 | import java.awt.event.MouseAdapter; | |
14 | import java.awt.event.MouseEvent; | |
15 | ||
16 | /** | |
17 | * A scrollpane like component, where the scroll bars are floating over the | |
18 | * scrollable view to indicate the current scroll positions. | |
19 | * The scroll indicators appear smoothly during scroll events and disappear | |
20 | * smoothly afterward. | |
21 | * <p> | |
22 | * The scrollbars can be dragged just as normal.</p> | |
23 | * <p> | |
24 | * The usage is similar to a classic scrollpane.</p> | |
25 | * | |
26 | * @author Jolly Littlebottom | |
27 | */ | |
28 | public class JScrollIndicator extends JLayeredPane { | |
29 | | |
30 | /** | |
31 | * Log4j logger sent to view. | |
32 | */ | |
33 | private static final Logger LOGGER = LogManager.getRootLogger(); | |
34 | ||
35 | private static final int SCROLL_BAR_ALPHA_ROLLOVER = 100; | |
36 | private static final int SCROLL_BAR_ALPHA = 25; | |
37 | ||
38 | private static final Color THUMB_COLOR = Color.DARK_GRAY; | |
39 | private static final int THUMB_THICKNESS = 15; | |
40 | private static final int THUMB_MIN_SIZE = 48; | |
41 | private static final int THUMB_MARGIN = 0; | |
42 | ||
43 | private final JScrollPane scrollPane; | |
44 | private final ControlPanel controlPanel; | |
45 | ||
46 | /** | |
47 | * Creates a <code>JScrollIndicator</code> that displays the contents of the | |
48 | * specified component, where both horizontal and vertical scrollbars appear | |
49 | * whenever the component's contents are larger than the view and scrolling | |
50 | * in underway or the mouse is over the scrollbar position. | |
51 | * | |
52 | * see #setViewportView | |
53 | * @param view the component to display in the scrollpane's viewport | |
54 | */ | |
55 | public JScrollIndicator(final JComponent view) { | |
56 | this(view, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); | |
57 | } | |
58 | | |
59 | public JScrollIndicator(final JComponent view, int scrollPaneConstants) { | |
60 | this(view, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, scrollPaneConstants); | |
61 | } | |
62 | ||
63 | /** | |
64 | * Creates a <code>JScrollIndicator</code> that displays the view component | |
65 | * in a viewport whose view position can be controlled with a pair of | |
66 | * scrollbars. | |
67 | * The scrollbar policies specify when the scrollbars are displayed, | |
68 | * For example, if <code>vsbPolicy</code> is | |
69 | * <code>JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED</code> | |
70 | * then the vertical scrollbar only appears if the view doesn't fit | |
71 | * vertically. The available policy settings are listed at | |
72 | * {link #JScrollPane.setVerticalScrollBarPolicy} and | |
73 | * {link #JScrollPane.setHorizontalScrollBarPolicy}. | |
74 | * | |
75 | * @param view the component to display in the scrollpanes viewport | |
76 | * @param vsbPolicy an integer that specifies the vertical scrollbar policy | |
77 | * @param hsbPolicy an integer that specifies the horizontal scrollbar policy | |
78 | */ | |
79 | public JScrollIndicator(final JComponent view, int vsbPolicy, int hsbPolicy) { | |
80 | | |
81 | this.scrollPane = new JScrollPane(view, vsbPolicy, hsbPolicy); | |
82 |
1
1. <init> : removed call to javax/swing/JScrollPane::setBorder → NO_COVERAGE |
this.scrollPane.setBorder(BorderFactory.createEmptyBorder()); |
83 |
1
1. <init> : removed call to com/jsql/view/swing/scrollpane/JScrollIndicator::add → NO_COVERAGE |
this.add(this.scrollPane, JLayeredPane.DEFAULT_LAYER); |
84 | ||
85 | this.controlPanel = new ControlPanel(this.scrollPane); | |
86 |
1
1. <init> : removed call to com/jsql/view/swing/scrollpane/JScrollIndicator::add → NO_COVERAGE |
this.add(this.controlPanel, JLayeredPane.PALETTE_LAYER); |
87 | ||
88 |
1
1. <init> : removed call to com/jsql/view/swing/scrollpane/JScrollIndicator::addComponentListener → NO_COVERAGE |
this.addComponentListener( |
89 | new ComponentAdapter() { | |
90 | | |
91 | @Override | |
92 | public void componentResized(ComponentEvent e) { | |
93 | | |
94 | // listen to changes of JLayeredPane size | |
95 |
1
1. componentResized : removed call to javax/swing/JScrollPane::setSize → NO_COVERAGE |
JScrollIndicator.this.scrollPane.setSize(JScrollIndicator.this.getSize()); |
96 |
1
1. componentResized : removed call to javax/swing/JViewport::revalidate → NO_COVERAGE |
JScrollIndicator.this.scrollPane.getViewport().revalidate(); |
97 |
1
1. componentResized : removed call to com/jsql/view/swing/scrollpane/JScrollIndicator$ControlPanel::setSize → NO_COVERAGE |
JScrollIndicator.this.controlPanel.setSize(JScrollIndicator.this.getSize()); |
98 |
1
1. componentResized : removed call to com/jsql/view/swing/scrollpane/JScrollIndicator$ControlPanel::revalidate → NO_COVERAGE |
JScrollIndicator.this.controlPanel.revalidate(); |
99 | } | |
100 | } | |
101 | ); | |
102 | } | |
103 | ||
104 | /** | |
105 | * Returns the scroll pane used by this scroll indicator. | |
106 | * Use carefully (e.g. to set unit increments) because not all changes have an | |
107 | * effect. You have to write listeners in these cases (e.g. for changing the | |
108 | * scrollbar policy) | |
109 | * | |
110 | * @return | |
111 | */ | |
112 | public JScrollPane getScrollPane() { | |
113 |
1
1. getScrollPane : replaced return value with null for com/jsql/view/swing/scrollpane/JScrollIndicator::getScrollPane → NO_COVERAGE |
return this.scrollPane; |
114 | } | |
115 | ||
116 | private class ControlPanel extends JPanel { | |
117 | ||
118 | private ControlPanel(JScrollPane scrollPane) { | |
119 | | |
120 |
1
1. <init> : removed call to com/jsql/view/swing/scrollpane/JScrollIndicator$ControlPanel::setLayout → NO_COVERAGE |
this.setLayout(new BorderLayout()); |
121 |
1
1. <init> : removed call to com/jsql/view/swing/scrollpane/JScrollIndicator$ControlPanel::setOpaque → NO_COVERAGE |
this.setOpaque(false); |
122 | ||
123 | JMyScrollBar vScrollBar = new JMyScrollBar(Adjustable.VERTICAL); | |
124 |
1
1. <init> : removed call to javax/swing/JScrollPane::setVerticalScrollBar → NO_COVERAGE |
scrollPane.setVerticalScrollBar(vScrollBar); |
125 |
1
1. <init> : removed call to javax/swing/JScrollPane::remove → NO_COVERAGE |
scrollPane.remove(vScrollBar); |
126 | | |
127 |
1
1. <init> : negated conditional → NO_COVERAGE |
if (scrollPane.getVerticalScrollBarPolicy() != ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER) { |
128 |
1
1. <init> : removed call to com/jsql/view/swing/scrollpane/JScrollIndicator$ControlPanel::add → NO_COVERAGE |
this.add(vScrollBar, BorderLayout.EAST); |
129 | } | |
130 | ||
131 | JMyScrollBar hScrollBar = new JMyScrollBar(Adjustable.HORIZONTAL); | |
132 |
1
1. <init> : removed call to javax/swing/JScrollPane::setHorizontalScrollBar → NO_COVERAGE |
scrollPane.setHorizontalScrollBar(hScrollBar); |
133 |
1
1. <init> : removed call to javax/swing/JScrollPane::remove → NO_COVERAGE |
scrollPane.remove(hScrollBar); |
134 | | |
135 |
1
1. <init> : negated conditional → NO_COVERAGE |
if (scrollPane.getHorizontalScrollBarPolicy() != ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER) { |
136 |
1
1. <init> : removed call to com/jsql/view/swing/scrollpane/JScrollIndicator$ControlPanel::add → NO_COVERAGE |
this.add(hScrollBar, BorderLayout.SOUTH); |
137 | } | |
138 | } | |
139 | } | |
140 | ||
141 | private class JMyScrollBar extends JScrollBar { | |
142 | ||
143 | protected final transient MyScrollBarUI scrollUI; | |
144 | ||
145 | public JMyScrollBar(int direction) { | |
146 | | |
147 | super(direction); | |
148 | ||
149 | this.scrollUI = new MyScrollBarUI(this); | |
150 |
1
1. <init> : removed call to javax/swing/JScrollBar::setUI → NO_COVERAGE |
super.setUI(this.scrollUI); |
151 |
1
1. <init> : removed call to com/jsql/view/swing/scrollpane/JScrollIndicator$JMyScrollBar::setUnitIncrement → NO_COVERAGE |
this.setUnitIncrement(64); |
152 | int size = THUMB_THICKNESS + THUMB_MARGIN; | |
153 |
1
1. <init> : removed call to com/jsql/view/swing/scrollpane/JScrollIndicator$JMyScrollBar::setPreferredSize → NO_COVERAGE |
this.setPreferredSize(new Dimension(size, size)); |
154 |
1
1. <init> : removed call to com/jsql/view/swing/scrollpane/JScrollIndicator$MyScrollBarUI::setVisible → NO_COVERAGE |
this.scrollUI.setVisible(); |
155 | | |
156 |
1
1. <init> : removed call to com/jsql/view/swing/scrollpane/JScrollIndicator$JMyScrollBar::addMouseListener → NO_COVERAGE |
this.addMouseListener(new MouseAdapter() { |
157 | | |
158 | @Override | |
159 | public void mouseEntered(MouseEvent e) { | |
160 |
1
1. mouseEntered : removed call to com/jsql/view/swing/scrollpane/JScrollIndicator$MyScrollBarUI::setVisible → NO_COVERAGE |
JMyScrollBar.this.scrollUI.setVisible(); |
161 | } | |
162 | ||
163 | @Override | |
164 | public void mouseExited(MouseEvent e) { | |
165 |
1
1. mouseExited : removed call to com/jsql/view/swing/scrollpane/JScrollIndicator$MyScrollBarUI::setVisible → NO_COVERAGE |
JMyScrollBar.this.scrollUI.setVisible(); |
166 | } | |
167 | }); | |
168 | ||
169 |
2
1. <init> : removed call to com/jsql/view/swing/scrollpane/JScrollIndicator$JMyScrollBar::addAdjustmentListener → NO_COVERAGE 2. lambda$new$0 : removed call to com/jsql/view/swing/scrollpane/JScrollIndicator$MyScrollBarUI::setVisible → NO_COVERAGE |
this.addAdjustmentListener(adjustmentEvent -> this.scrollUI.setVisible()); |
170 | } | |
171 | ||
172 | @Override | |
173 | public void setUI(ScrollBarUI ui) { | |
174 | // Nothing | |
175 | } | |
176 | ||
177 | @Override | |
178 | public void updateUI() { | |
179 | // Nothing | |
180 | } | |
181 | ||
182 | @Override | |
183 | public void paint(Graphics g) { | |
184 |
1
1. paint : removed call to com/jsql/view/swing/scrollpane/JScrollIndicator$MyScrollBarUI::paintThumb → NO_COVERAGE |
this.scrollUI.paintThumb(g); // just the thumb |
185 | } | |
186 | ||
187 | @Override | |
188 | public void repaint(Rectangle r) { | |
189 | | |
190 | JScrollIndicator scrollIndicator = JScrollIndicator.this; | |
191 | | |
192 | // Fix #15956: NullPointerException on convertRectangle() | |
193 | try { | |
194 | var rect = SwingUtilities.convertRectangle(this, r, scrollIndicator); | |
195 |
1
1. repaint : removed call to java/awt/Rectangle::grow → NO_COVERAGE |
rect.grow(1, 1); |
196 | // ensure for a translucent thumb, that the view is first painted | |
197 |
1
1. repaint : removed call to com/jsql/view/swing/scrollpane/JScrollIndicator::repaint → NO_COVERAGE |
scrollIndicator.repaint(rect); |
198 | | |
199 | } catch (NullPointerException e) { | |
200 | LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e); | |
201 | } | |
202 | } | |
203 | } | |
204 | ||
205 | public class MyScrollBarUI extends BasicScrollBarUI { | |
206 | | |
207 | private final JMyScrollBar myScrollBar; | |
208 | private int alpha = 0; | |
209 | ||
210 | private MyScrollBarUI(JMyScrollBar scrollBar) { | |
211 | this.myScrollBar = scrollBar; | |
212 | } | |
213 | ||
214 | @Override | |
215 | protected void installComponents() { | |
216 | | |
217 | this.incrButton = new JButton(); | |
218 | this.decrButton = new JButton(); | |
219 | | |
220 |
1
1. installComponents : negated conditional → NO_COVERAGE |
if (this.myScrollBar.getOrientation() == Adjustable.HORIZONTAL) { |
221 | | |
222 | int size = THUMB_THICKNESS + THUMB_MARGIN; // let lower right corner empty | |
223 |
1
1. installComponents : removed call to javax/swing/JButton::setPreferredSize → NO_COVERAGE |
this.incrButton.setPreferredSize(new Dimension(size, size)); |
224 | | |
225 | } else { | |
226 |
1
1. installComponents : removed call to javax/swing/JButton::setPreferredSize → NO_COVERAGE |
this.incrButton.setPreferredSize(new Dimension(THUMB_MARGIN, THUMB_MARGIN)); |
227 | } | |
228 | | |
229 |
1
1. installComponents : removed call to javax/swing/JButton::setPreferredSize → NO_COVERAGE |
this.decrButton.setPreferredSize(new Dimension(THUMB_MARGIN, THUMB_MARGIN)); |
230 | } | |
231 | ||
232 | @Override | |
233 | protected void installDefaults() { | |
234 | | |
235 |
1
1. installDefaults : removed call to javax/swing/plaf/basic/BasicScrollBarUI::installDefaults → NO_COVERAGE |
super.installDefaults(); |
236 | ||
237 | // ensure the minimum size of the thumb | |
238 | int w = this.minimumThumbSize.width; | |
239 | int h = this.minimumThumbSize.height; | |
240 | | |
241 |
1
1. installDefaults : negated conditional → NO_COVERAGE |
if (this.myScrollBar.getOrientation() == Adjustable.VERTICAL) { |
242 | h = Math.max(h, Math.min(this.maximumThumbSize.height, THUMB_MIN_SIZE)); | |
243 | } else { | |
244 | w = Math.max(w, Math.min(this.maximumThumbSize.width, THUMB_MIN_SIZE)); | |
245 | } | |
246 | | |
247 | this.minimumThumbSize = new Dimension(w, h); | |
248 | } | |
249 | ||
250 | private void paintThumb(Graphics g) { | |
251 | | |
252 |
1
1. paintThumb : negated conditional → NO_COVERAGE |
int alphaThumb = this.isThumbRollover() |
253 | ? SCROLL_BAR_ALPHA_ROLLOVER | |
254 | : SCROLL_BAR_ALPHA; | |
255 | ||
256 |
1
1. paintThumb : removed call to java/awt/Graphics::setColor → NO_COVERAGE |
g.setColor( |
257 | new Color( | |
258 | this.getAlphaColor().getRed(), | |
259 | this.getAlphaColor().getGreen(), | |
260 | this.getAlphaColor().getBlue(), | |
261 | alphaThumb | |
262 | ) | |
263 | ); | |
264 | | |
265 | Rectangle thumbBounds = this.getThumbBounds(); | |
266 | ||
267 | int x = thumbBounds.x; | |
268 | int y = thumbBounds.y; | |
269 | int w = thumbBounds.width; | |
270 | int h = thumbBounds.height; | |
271 | ||
272 |
1
1. paintThumb : negated conditional → NO_COVERAGE |
if (this.myScrollBar.getOrientation() == Adjustable.VERTICAL) { |
273 |
1
1. paintThumb : Changed increment from 0 to 0 → NO_COVERAGE |
w -= THUMB_MARGIN; |
274 | } else { | |
275 |
1
1. paintThumb : Changed increment from 0 to 0 → NO_COVERAGE |
h -= THUMB_MARGIN; |
276 | } | |
277 | ||
278 |
1
1. paintThumb : removed call to java/awt/Graphics::fillRect → NO_COVERAGE |
g.fillRect(x, y, w, h); |
279 | } | |
280 | ||
281 | private Color getAlphaColor() { | |
282 | | |
283 |
1
1. getAlphaColor : negated conditional → NO_COVERAGE |
if (this.alpha == 100) { |
284 |
1
1. getAlphaColor : replaced return value with null for com/jsql/view/swing/scrollpane/JScrollIndicator$MyScrollBarUI::getAlphaColor → NO_COVERAGE |
return JScrollIndicator.THUMB_COLOR; |
285 | } | |
286 | | |
287 |
1
1. getAlphaColor : Replaced bitwise AND with OR → NO_COVERAGE |
int rgb = JScrollIndicator.THUMB_COLOR.getRGB() & 0xFFFFFF; // color without alpha values |
288 |
4
1. getAlphaColor : Replaced integer multiplication with division → NO_COVERAGE 2. getAlphaColor : Replaced bitwise OR with AND → NO_COVERAGE 3. getAlphaColor : Replaced Shift Left with Shift Right → NO_COVERAGE 4. getAlphaColor : Replaced integer division with multiplication → NO_COVERAGE |
rgb |= (this.alpha / 100 * 255) << 24; // add alpha value |
289 | | |
290 |
1
1. getAlphaColor : replaced return value with null for com/jsql/view/swing/scrollpane/JScrollIndicator$MyScrollBarUI::getAlphaColor → NO_COVERAGE |
return new Color(rgb, true); |
291 | } | |
292 | ||
293 | public void setAlpha(int alpha) { | |
294 | | |
295 | this.alpha = alpha; | |
296 |
1
1. setAlpha : removed call to com/jsql/view/swing/scrollpane/JScrollIndicator$JMyScrollBar::repaint → NO_COVERAGE |
this.myScrollBar.repaint(this.getThumbBounds()); |
297 | } | |
298 | ||
299 | public void setVisible() { | |
300 |
1
1. setVisible : removed call to com/jsql/view/swing/scrollpane/JScrollIndicator$JMyScrollBar::repaint → NO_COVERAGE |
this.myScrollBar.repaint(this.getThumbBounds()); |
301 | } | |
302 | } | |
303 | } | |
Mutations | ||
82 |
1.1 |
|
83 |
1.1 |
|
86 |
1.1 |
|
88 |
1.1 |
|
95 |
1.1 |
|
96 |
1.1 |
|
97 |
1.1 |
|
98 |
1.1 |
|
113 |
1.1 |
|
120 |
1.1 |
|
121 |
1.1 |
|
124 |
1.1 |
|
125 |
1.1 |
|
127 |
1.1 |
|
128 |
1.1 |
|
132 |
1.1 |
|
133 |
1.1 |
|
135 |
1.1 |
|
136 |
1.1 |
|
150 |
1.1 |
|
151 |
1.1 |
|
153 |
1.1 |
|
154 |
1.1 |
|
156 |
1.1 |
|
160 |
1.1 |
|
165 |
1.1 |
|
169 |
1.1 2.2 |
|
184 |
1.1 |
|
195 |
1.1 |
|
197 |
1.1 |
|
220 |
1.1 |
|
223 |
1.1 |
|
226 |
1.1 |
|
229 |
1.1 |
|
235 |
1.1 |
|
241 |
1.1 |
|
252 |
1.1 |
|
256 |
1.1 |
|
272 |
1.1 |
|
273 |
1.1 |
|
275 |
1.1 |
|
278 |
1.1 |
|
283 |
1.1 |
|
284 |
1.1 |
|
287 |
1.1 |
|
288 |
1.1 2.2 3.3 4.4 |
|
290 |
1.1 |
|
296 |
1.1 |
|
300 |
1.1 |