1 package com.sun.java.swing.plaf.windows;
2
3 import com.sun.java.swing.plaf.windows.WindowsFileChooserUI.WindowsFileListUI;
4 import java.awt.Color;
5 import java.awt.Component;
6 import java.awt.Dimension;
7 import java.awt.Graphics;
8 import java.awt.Insets;
9 import java.awt.Rectangle;
10 import static java.awt.Toolkit.getDefaultToolkit;
11 import java.awt.event.MouseAdapter;
12 import java.awt.event.MouseEvent;
13 import java.beans.PropertyChangeEvent;
14 import java.beans.PropertyChangeListener;
15 import java.io.File;
16 import java.io.FileNotFoundException;
17 import java.io.UncheckedIOException;
18 import java.util.Arrays;
19 import java.util.Collections;
20 import static java.util.Collections.emptyList;
21 import java.util.Enumeration;
22 import java.util.HashMap;
23 import java.util.List;
24 import java.util.Map;
25 import static java.util.stream.Collectors.toList;
26 import javax.swing.JComponent;
27 import javax.swing.JFileChooser;
28 import javax.swing.JTree;
29 import javax.swing.filechooser.FileSystemView;
30 import javax.swing.tree.DefaultTreeCellRenderer;
31 import javax.swing.tree.DefaultTreeModel;
32 import javax.swing.tree.TreeNode;
33 import javax.swing.tree.TreePath;
34 import sun.awt.shell.ShellFolder;
35 import static sun.awt.shell.ShellFolder.getShellFolder;
36 import static com.sun.java.swing.plaf.windows.WindowsFileChooserUI.WindowsFileListUI.ITEM_SELECTED_COLOR;
37 import static com.sun.java.swing.plaf.windows.WindowsFileChooserUI.WindowsFileListUI.ITEM_HOVERED_COLOR;
38 import java.awt.AlphaComposite;
39 import java.awt.Composite;
40 import java.awt.Cursor;
41 import java.awt.Graphics2D;
42 import java.awt.Image;
43 import java.awt.Point;
44 import java.awt.image.BufferedImage;
45 import static java.lang.Boolean.TRUE;
46 import static java.time.Duration.between;
47 import java.time.Instant;
48 import static java.time.Instant.now;
49 import java.util.ArrayList;
50 import java.util.function.Supplier;
51 import javax.swing.Icon;
52 import javax.swing.ImageIcon;
53 import static javax.swing.JFileChooser.FILE_SYSTEM_VIEW_CHANGED_PROPERTY;
54 import javax.swing.JScrollPane;
55 import javax.swing.JSeparator;
56 import javax.swing.JViewport;
57 import javax.swing.Timer;
58 import javax.swing.UIManager;
59 import javax.swing.filechooser.FileSystemView.EssentialFolderSection;
60 import javax.swing.tree.TreeSelectionModel;
61 import static javax.swing.tree.TreeSelectionModel.SINGLE_TREE_SELECTION;
62
63 public class WindowsNavigationTree extends JTree implements PropertyChangeListener {
64
65 private int hover = -1;
66 private Point mousePoint;
67
68 private final JFileChooser fileChooser;
69 private final FileSystemView view;
70
71 private static final Icon EXPAND_ICON = new ImageIcon(initRightArrowNormalImage());
72 private static final Icon COLLAPSE_ICON = new ImageIcon(initDownArrowNormalImage());
73
74 private static final Icon EXPAND_ICON_HOVER = new ImageIcon(initRightArrowHoverImage());
75 private static final Icon COLLAPSE_ICON_HOVER = new ImageIcon(initDownArrowHoverImage());
76
77 public WindowsNavigationTree(WindowsFileChooserUI fileChooserUI) {
78 this.fileChooser = fileChooserUI.getFileChooser();
79 this.view = fileChooser.getFileSystemView();
80
81 super.setRootVisible(false);
82 super.setUI(new NavTreeUI());
83 super.addMouseListener(new MouseAdapter() {
84
85 @Override
86 public void mouseExited(MouseEvent e) {
87 hover = -1;
88 setCursor(Cursor.getDefaultCursor());
89 repaint();
90 }
91
92 });
93 super.addMouseMotionListener(new MouseAdapter() {
94 @Override
95 public void mouseMoved(MouseEvent e) {
96 mousePoint = e.getPoint();
97
98 int row = getRowForLocation(e.getX(), e.getY());
99 TreePath path = getPathForRow(row);
100 if (row == -1 || path.getLastPathComponent() instanceof EmptyNode) {
101 hover = -1;
102 setCursor(Cursor.getDefaultCursor());
103 } else {
104 hover = row;
105 Rectangle rowBounds = ((NavTreeUI) getUI()).getStrictBounds(path);
106 if (rowBounds.contains(mousePoint)) {
107 setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
108 } else {
109 setCursor(Cursor.getDefaultCursor());
110 }
111 }
112 repaint();
113 }
114 });
115 super.setCellRenderer(new DefaultTreeCellRenderer() {
116
117 private boolean isSeparatorNode;
118
119 @Override
120 public Color getBackgroundNonSelectionColor() {
121 return null;
122 }
123
124 @Override
125 public Color getBackground() {
126 return null;
127 }
128
129 @Override
130 public Component getTreeCellRendererComponent(JTree tree, Object value,
131 boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) {
132 isSeparatorNode = false;
133
134 if (value instanceof RootNode) {
135 return this;
136 }
137 if (value instanceof EmptyNode) {
138 if (value instanceof SeparatorNode) {
139 isSeparatorNode = true;
140 }
141 setText("");
142 setIcon(null);
143 return this;
144 }
145
146 File folder = ((FolderTreeNode) value).folder;
147
148 setIcon(view.getSystemIcon(folder));
149 setText(view.getSystemDisplayName(folder));
150 return this;
151 }
152
153 @Override
154 public Dimension getPreferredSize() {
155 if (getText().isEmpty() && getIcon() == null) {
156 return new Dimension(0, isSeparatorNode ? 18 : 12);
157 } else {
158 Dimension result = super.getPreferredSize();;
159 result.height = 24;
160 return result;
161 }
162 }
163
164 });
165
166 RootNode rootNode = new RootNode();
167 super.setModel(new DefaultTreeModel(rootNode));
168
169 int childCount = rootNode.getChildCount();
170 for (int i = childCount - 1; i >= 0; i--) {
171 if (i != childCount - 2) {
172 super.expandRow(i);
173 }
174 }
175
176 super.setRowHeight(0);
177
178 getSelectionModel().addTreeSelectionListener(evt -> {
179 TreePath path = evt.getNewLeadSelectionPath();
180 if (path == null) {
181 return;
182 }
183
184 if (path.getLastPathComponent() instanceof EmptyNode) {
185 // disable selecting an EmptyNode
186
187 if (evt.getOldLeadSelectionPath() == null
188 || !(evt.getOldLeadSelectionPath().
189 getLastPathComponent() instanceof EmptyNode)) {
190 setSelectionPath(evt.getOldLeadSelectionPath());
191 }
192 return;
193 }
194
195 FolderTreeNode node = (FolderTreeNode) path.getLastPathComponent();
196 fileChooser.setCurrentDirectory(node.folder);
197 });
198
199 getSelectionModel().setSelectionMode(SINGLE_TREE_SELECTION);
200
201 setTransferHandler(fileChooser.getTransferHandler());
202 putClientProperty("Tree.enableDoubleClickOnExpandControl", TRUE);
203
204 // these 2 values are used in BasicFileChooserUI's FileTransferHandler
205 putClientProperty("FileChooser.instance", fileChooser);
206 putClientProperty("FileChooser.supportsFileDrop", TRUE);
207 }
208
209 @Override
210 public void propertyChange(PropertyChangeEvent evt) {
211 if (evt.getPropertyName().equals("transferHandler")) {
212 setTransferHandler(fileChooser.getTransferHandler());
213 }
214 if (evt.getPropertyName().equals((FILE_SYSTEM_VIEW_CHANGED_PROPERTY))) {
215 repaint();
216 }
217 }
218
219 public TreePath getPathForLocation(int x, int y) {
220 return getClosestPathForLocation(x, y);
221 }
222
223 @Override
224 public Dimension getPreferredSize() {
225 Dimension result = super.getPreferredSize();
226 result.width = 230;
227 return result;
228 }
229
230 private class RootNode implements TreeNode {
231
232 private final List<TreeNode> children = new ArrayList<>();
233 private FileSystemView.EssentialFolderSection[] prevCategories;
234
235 private FileSystemView.EssentialFolderSection[] getCategories() {
236 return fileChooser.getFileSystemView().getEssentialFolders();
237 }
238
239 private boolean isModified() {
240 FileSystemView.EssentialFolderSection[] categories = getCategories();
241 if (Arrays.equals(prevCategories, categories)) {
242 return false;
243 }
244
245 prevCategories = categories;
246 return true;
247 }
248
249 private void validateChildrenCache() {
250 if (isModified()) {
251 EssentialFolderSection[] categories = getCategories();
252
253 children.clear();
254 children.add(new EmptyNode(this));
255 for (EssentialFolderSection category : categories) {
256 children.add(new SeparatorNode(this));
257
258 int firstEmptyNodeLocation = children.size();
259 for (File folder : category.getFolders()) {
260 children.add(new EmptyNode(this));
261 children.add(FOLDER_NODE_CACHE.computeIfAbsent(folder,
262 f -> new FolderTreeNode(this, f, true)));
263 }
264
265 children.remove(firstEmptyNodeLocation);
266 }
267 children.add(new EmptyNode(this));
268 children.remove(1); // remove the first separator
269 }
270 }
271
272 @Override
273 public TreeNode getChildAt(int childIndex) {
274 validateChildrenCache();
275 return children.get(childIndex);
276 }
277
278 @Override
279 public int getChildCount() {
280 validateChildrenCache();
281 return children.size();
282 }
283
284 @Override
285 public TreeNode getParent() {
286 return null;
287 }
288
289 @Override
290 public int getIndex(TreeNode node) {
291 return -1;
292 }
293
294 @Override
295 public boolean getAllowsChildren() {
296 return true;
297 }
298
299 @Override
300 public boolean isLeaf() {
301 return false;
302 }
303
304 @Override
305 public Enumeration children() {
306 validateChildrenCache();
307 return Collections.enumeration(children);
308 }
309
310 }
311 private static final Map<File, FolderTreeNode> FOLDER_NODE_CACHE = new HashMap<>();
312
313 public class FolderTreeNode implements TreeNode {
314
315 public final File folder;
316 private final TreeNode parent;
317 private final boolean recursiveChildren;
318
319 private FolderTreeNode(TreeNode parent, File folder, boolean recursiveChildren) {
320 assert folder != null;
321
322 try {
323 while (fileChooser.getFileSystemView().isLink(folder)) {
324 folder = fileChooser.getFileSystemView().getLinkLocation(folder);
325 }
326 } catch (FileNotFoundException ex) {
327 throw new UncheckedIOException(ex);
328 }
329
330 this.parent = parent;
331 this.folder = folder;
332 this.recursiveChildren = recursiveChildren;
333 }
334
335 private List<File> childrenCache;
336
337 @SuppressWarnings("ReturnOfCollectionOrArrayField")
338 private List<File> getChildren() {
339 if (childrenCache != null) {
340 return childrenCache;
341 }
342
343 if (!recursiveChildren && !(parent instanceof RootNode)) {
344 return childrenCache = emptyList();
345 }
346
347 Object hidingValue = getDefaultToolkit().getDesktopProperty("awt.file.showHiddenFiles");
348 File[] files = view.getFiles(folder, (boolean) hidingValue);
349 return childrenCache = Arrays.stream(files).
350 filter(view::isTraversable).
351 collect(toList());
352 }
353
354 @Override
355 public int getChildCount() {
356 return getChildren().size();
357 }
358
359 @Override
360 public TreeNode getChildAt(int childIndex) {
361 File file = getChildren().get(childIndex);
362 return FOLDER_NODE_CACHE.computeIfAbsent(file,
363 f -> new FolderTreeNode(this, f, recursiveChildren));
364 }
365
366 @Override
367 public TreeNode getParent() {
368 return parent;
369 }
370
371 @Override
372 public int getIndex(TreeNode node) {
373 return ((FolderTreeNode) node).getChildren().indexOf(folder);
374 }
375
376 @Override
377 public boolean getAllowsChildren() {
378 return true;
379 }
380
381 @Override
382 public boolean isLeaf() {
383 if (!recursiveChildren && !(parent instanceof RootNode)) {
384 return true;
385 }
386
387 if (childrenCache == null) {
388 return false;
389 }
390 return childrenCache.isEmpty();
391 }
392
393 @Override
394 public Enumeration children() {
395 return Collections.enumeration(getChildren());
396 }
397
398 }
399
400 private class EmptyNode implements TreeNode {
401
402 private final TreeNode parent;
403
404 private EmptyNode(TreeNode parent) {
405 this.parent = parent;
406 }
407
408 @Override
409 public TreeNode getChildAt(int childIndex) {
410 throw new UnsupportedOperationException("an EmptyNode doesn't have children");
411 }
412
413 @Override
414 public int getChildCount() {
415 return 0;
416 }
417
418 @Override
419 public TreeNode getParent() {
420 return parent;
421 }
422
423 @Override
424 public int getIndex(TreeNode node) {
425 throw new UnsupportedOperationException("an EmptyNode doesn't have children");
426 }
427
428 @Override
429 public boolean getAllowsChildren() {
430 return false;
431 }
432
433 @Override
434 public boolean isLeaf() {
435 return true;
436 }
437
438 @Override
439 public Enumeration children() {
440 return Collections.emptyEnumeration();
441 }
442 }
443
444 public class SeparatorNode extends EmptyNode {
445
446 public SeparatorNode(TreeNode parent) {
447 super(parent);
448 }
449
450 }
451
452 private class NavTreeUI extends WindowsTreeUI {
453
454 private boolean expandControlHover;
455
456 @Override
457 protected void installDefaults() {
458 super.installDefaults();
459 setLeftChildIndent(0);
460
461 setExpandedIcon(new AnimatableIcon(COLLAPSE_ICON));
462 setCollapsedIcon(new AnimatableIcon(EXPAND_ICON));
463 }
464
465 @Override
466 protected void installListeners() {
467 super.installListeners();
468 tree.addMouseListener(new MouseAdapter() {
469
470 private final Timer animationTimer = new Timer(30, evt -> onAnimFrame());
471
472 @Override
473 public void mouseEntered(MouseEvent e) {
474 Instant start = now();
475 iconAlphaSupplier = () -> ((float) between(start, now()).toMillis()) / 300;
476 animationTimer.start();
477 }
478
479 @Override
480 public void mouseExited(MouseEvent e) {
481 Instant start = now();
482 iconAlphaSupplier = () -> 1 - ((float) between(start, now()).toMillis()) / 1000;
483 animationTimer.start();
484 }
485
486 private void onAnimFrame() {
487 if (iconAlphaSupplier.get() < 0) {
488 animationTimer.stop();
489 iconAlphaSupplier = () -> 0f;
490 } else if (iconAlphaSupplier.get() > 1) {
491 animationTimer.stop();
492 iconAlphaSupplier = () -> 1f;
493 } else {
494 tree.repaint();
495 }
496 }
497 });
498 }
499
500 @Override
501 protected void updateDepthOffset() {
502 depthOffset = 1;
503 }
504
505 @Override
506 protected boolean shouldPaintExpandControl(TreePath path, int row,
507 boolean isExpanded,
508 boolean hasBeenExpanded,
509 boolean isLeaf) {
510 return !isLeaf;
511 }
512
513 public Rectangle getStrictBounds(TreePath path) {
514 return super.getPathBounds(WindowsNavigationTree.this, path);
515 }
516
517 @Override
518 public Rectangle getPathBounds(JTree tree, TreePath path) {
519 Rectangle result = super.getPathBounds(tree, path);
520 if (result == null) {
521 return null;
522 }
523
524 result.x = 0;
525 result.width = getWidth();
526 return result;
527 }
528
529 @Override
530 protected void paintExpandControl(Graphics g, Rectangle clipBounds,
531 Insets insets, Rectangle bounds, TreePath path,
532 int row, boolean isExpanded,
533 boolean hasBeenExpanded, boolean isLeaf) {
534 paintBackground(g, row, bounds);
535 expandControlHover = row == hover && isLocationInExpandControl(path,
536 mousePoint.x, mousePoint.y);
537 try {
538 super.paintExpandControl(g, clipBounds, insets, bounds, path, row, isExpanded, hasBeenExpanded, isLeaf);
539 } finally {
540 expandControlHover = false;
541 }
542 }
543
544 @Override
545 public Icon getCollapsedIcon() {
546 if (expandControlHover) {
547 return EXPAND_ICON_HOVER;
548 }
549 return super.getCollapsedIcon();
550 }
551
552 @Override
553 public Icon getExpandedIcon() {
554 if (expandControlHover) {
555 return COLLAPSE_ICON_HOVER;
556 }
557 return super.getExpandedIcon();
558 }
559
560 @Override
561 protected void paintRow(Graphics g, Rectangle clipBounds, Insets insets, Rectangle bounds, TreePath path, int row, boolean isExpanded, boolean hasBeenExpanded, boolean isLeaf) {
562 if (!shouldPaintExpandControl(path, row, isExpanded, hasBeenExpanded, isLeaf)) {
563 paintBackground(g, row, bounds);
564 }
565
566 if (path.getLastPathComponent() instanceof SeparatorNode) {
567 bounds.y += bounds.height / 2 - 1;
568
569 g.setColor(UIManager.getColor("Separator.foreground"));
570 g.drawLine(0, bounds.y, tree.getWidth(), bounds.y);
571
572 g.setColor(UIManager.getColor("Separator.background"));
573 g.drawLine(0, bounds.y + 1, tree.getWidth(), bounds.y + 1);
574 return;
575 }
576
577 super.paintRow(g, clipBounds, insets, bounds, path, row, isExpanded, hasBeenExpanded, isLeaf);
578 }
579
580 private void paintBackground(Graphics g, int row, Rectangle bounds) {
581 if (hover == row) {
582 g.setColor(ITEM_HOVERED_COLOR);
583 g.fillRect(0, bounds.y, tree.getWidth(), bounds.height);
584 }
585 if (tree.isRowSelected(row)) {
586 g.setColor(ITEM_SELECTED_COLOR);
587 g.fillRect(0, bounds.y, tree.getWidth(), bounds.height);
588 if (hover == row) {
589 g.setColor(WindowsFileListUI.ITEM_SELECTED_BORDER_COLOR);
590 g.drawRect(0, bounds.y, tree.getWidth() - 1, tree.getRowHeight());
591 }
592 }
593 }
594
595 @Override
596 protected void paintHorizontalLine(Graphics g, JComponent c, int y, int left, int right) {
597 }
598
599 @Override
600 protected void paintVerticalLine(Graphics g, JComponent c, int x, int top, int bottom) {
601 }
602 }
603
604 private Supplier<Float> iconAlphaSupplier = () -> 0f;
605
606 private class AnimatableIcon implements Icon {
607
608 private final Icon icon;
609
610 public AnimatableIcon(Icon icon) {
611 this.icon = icon;
612 }
613
614 @Override
615 public void paintIcon(Component c, Graphics g, int x, int y) {
616 if (g instanceof Graphics2D) {
617 Graphics2D g2d = (Graphics2D) g;
618 Composite prevComposite = g2d.getComposite();
619
620 float alpha = Math.min(1, Math.max(0, iconAlphaSupplier.get()));
621 g2d.setComposite(AlphaComposite.SrcOver.derive(alpha));
622 icon.paintIcon(c, g, x, y);
623 g2d.setComposite(prevComposite);
624 } else {
625 icon.paintIcon(c, g, x, y);
626 }
627 }
628
629 @Override
630 public int getIconWidth() {
631 return icon.getIconWidth();
632 }
633
634 @Override
635 public int getIconHeight() {
636 return icon.getIconHeight();
637 }
638
639 }
640
641 private static Image initDownArrowNormalImage() {
642 BufferedImage img = new BufferedImage(9, 6, 6);
643 img.setRGB(0, 0, 1610612736);
644 img.setRGB(0, 1, -2013265920);
645 img.setRGB(0, 2, 83886080);
646 img.setRGB(0, 3, 14277081);
647 img.setRGB(0, 4, 14277081);
648 img.setRGB(0, 5, 14277081);
649 img.setRGB(1, 0, -2013265920);
650 img.setRGB(1, 1, -1073741824);
651 img.setRGB(1, 2, -2013265920);
652 img.setRGB(1, 3, 67108864);
653 img.setRGB(1, 4, 14277081);
654 img.setRGB(1, 5, 14277081);
655 img.setRGB(2, 0, 67108864);
656 img.setRGB(2, 1, -2013265920);
657 img.setRGB(2, 2, -1073741824);
658 img.setRGB(2, 3, -2013265920);
659 img.setRGB(2, 4, 67108864);
660 img.setRGB(2, 5, 14277081);
661 img.setRGB(3, 0, 14277081);
662 img.setRGB(3, 1, 67108864);
663 img.setRGB(3, 2, -2013265920);
664 img.setRGB(3, 3, -1073741824);
665 img.setRGB(3, 4, -2013265920);
666 img.setRGB(3, 5, 67108864);
667 img.setRGB(4, 0, 14277081);
668 img.setRGB(4, 1, 14277081);
669 img.setRGB(4, 2, 150994944);
670 img.setRGB(4, 3, -1291845632);
671 img.setRGB(4, 4, -1073741824);
672 img.setRGB(4, 5, 1577058304);
673 img.setRGB(5, 0, 14277081);
674 img.setRGB(5, 1, 67108864);
675 img.setRGB(5, 2, -2013265920);
676 img.setRGB(5, 3, -1073741824);
677 img.setRGB(5, 4, -2013265920);
678 img.setRGB(5, 5, 83886080);
679 img.setRGB(6, 0, 67108864);
680 img.setRGB(6, 1, -2013265920);
681 img.setRGB(6, 2, -1073741824);
682 img.setRGB(6, 3, -2013265920);
683 img.setRGB(6, 4, 67108864);
684 img.setRGB(6, 5, 14277081);
685 img.setRGB(7, 0, -2013265920);
686 img.setRGB(7, 1, -1073741824);
687 img.setRGB(7, 2, -2013265920);
688 img.setRGB(7, 3, 67108864);
689 img.setRGB(7, 4, 14277081);
690 img.setRGB(7, 5, 14277081);
691 img.setRGB(8, 0, 1593835520);
692 img.setRGB(8, 1, -2013265920);
693 img.setRGB(8, 2, 67108864);
694 img.setRGB(8, 3, 14277081);
695 img.setRGB(8, 4, 14277081);
696 img.setRGB(8, 5, 14277081);
697 return img;
698 }
699
700 private static Image initRightArrowNormalImage() {
701 BufferedImage img = new BufferedImage(6, 9, 6);
702 img.setRGB(0, 0, 754974720);
703 img.setRGB(0, 1, 1073741824);
704 img.setRGB(0, 2, 33554432);
705 img.setRGB(0, 6, 33554432);
706 img.setRGB(0, 7, 1073741824);
707 img.setRGB(0, 8, 754974720);
708 img.setRGB(1, 0, 1073741824);
709 img.setRGB(1, 1, 1493172224);
710 img.setRGB(1, 2, 1073741824);
711 img.setRGB(1, 3, 33554432);
712 img.setRGB(1, 5, 33554432);
713 img.setRGB(1, 6, 1073741824);
714 img.setRGB(1, 7, 1493172224);
715 img.setRGB(1, 8, 1073741824);
716 img.setRGB(2, 0, 33554432);
717 img.setRGB(2, 1, 1073741824);
718 img.setRGB(2, 2, 1493172224);
719 img.setRGB(2, 3, 1073741824);
720 img.setRGB(2, 4, 83886080);
721 img.setRGB(2, 5, 1073741824);
722 img.setRGB(2, 6, 1493172224);
723 img.setRGB(2, 7, 1073741824);
724 img.setRGB(2, 8, 33554432);
725 img.setRGB(3, 1, 33554432);
726 img.setRGB(3, 2, 1073741824);
727 img.setRGB(3, 3, 1493172224);
728 img.setRGB(3, 4, 1392508928);
729 img.setRGB(3, 5, 1493172224);
730 img.setRGB(3, 6, 1073741824);
731 img.setRGB(3, 7, 33554432);
732 img.setRGB(4, 2, 33554432);
733 img.setRGB(4, 3, 1073741824);
734 img.setRGB(4, 4, 1493172224);
735 img.setRGB(4, 5, 1073741824);
736 img.setRGB(4, 6, 33554432);
737 img.setRGB(5, 3, 33554432);
738 img.setRGB(5, 4, 721420288);
739 img.setRGB(5, 5, 33554432);
740 return img;
741 }
742
743 private static Image initDownArrowHoverImage() {
744 BufferedImage img = new BufferedImage(9, 6, 6);
745 img.setRGB(0, 0, 1879096822);
746 img.setRGB(0, 1, -1627341067);
747 img.setRGB(0, 2, 100702207);
748 img.setRGB(0, 3, 15070207);
749 img.setRGB(0, 4, 15070207);
750 img.setRGB(0, 5, 15070207);
751 img.setRGB(1, 0, -1627341067);
752 img.setRGB(1, 1, -536822282);
753 img.setRGB(1, 2, -1627341067);
754 img.setRGB(1, 3, 67156735);
755 img.setRGB(1, 4, 15070207);
756 img.setRGB(1, 5, 15070207);
757 img.setRGB(2, 0, 67156735);
758 img.setRGB(2, 1, -1627341067);
759 img.setRGB(2, 2, -536822282);
760 img.setRGB(2, 3, -1627341067);
761 img.setRGB(2, 4, 67156735);
762 img.setRGB(2, 5, 15070207);
763 img.setRGB(3, 0, 15070207);
764 img.setRGB(3, 1, 67156735);
765 img.setRGB(3, 2, -1627341067);
766 img.setRGB(3, 3, -536822282);
767 img.setRGB(3, 4, -1627341067);
768 img.setRGB(3, 5, 67156735);
769 img.setRGB(4, 0, 15070207);
770 img.setRGB(4, 1, 15070207);
771 img.setRGB(4, 2, 184600063);
772 img.setRGB(4, 3, -771703049);
773 img.setRGB(4, 4, -536822282);
774 img.setRGB(4, 5, 1828765174);
775 img.setRGB(5, 0, 15070207);
776 img.setRGB(5, 1, 67156735);
777 img.setRGB(5, 2, -1627341067);
778 img.setRGB(5, 3, -536822282);
779 img.setRGB(5, 4, -1627341067);
780 img.setRGB(5, 5, 100702207);
781 img.setRGB(6, 0, 67156735);
782 img.setRGB(6, 1, -1627341067);
783 img.setRGB(6, 2, -536822282);
784 img.setRGB(6, 3, -1627341067);
785 img.setRGB(6, 4, 67156735);
786 img.setRGB(6, 5, 15070207);
787 img.setRGB(7, 0, -1627341067);
788 img.setRGB(7, 1, -536822282);
789 img.setRGB(7, 2, -1627341067);
790 img.setRGB(7, 3, 67156735);
791 img.setRGB(7, 4, 15070207);
792 img.setRGB(7, 5, 15070207);
793 img.setRGB(8, 0, 1862319862);
794 img.setRGB(8, 1, -1627341067);
795 img.setRGB(8, 2, 67156735);
796 img.setRGB(8, 3, 15070207);
797 img.setRGB(8, 4, 15070207);
798 img.setRGB(8, 5, 15070207);
799 return img;
800 }
801
802 private static Image initRightArrowHoverImage() {
803 BufferedImage img = new BufferedImage(6, 9, 6);
804 img.setRGB(0, 0, 1409335286);
805 img.setRGB(0, 1, 1996537334);
806 img.setRGB(0, 2, 50374655);
807 img.setRGB(0, 3, 15070207);
808 img.setRGB(0, 4, 15070207);
809 img.setRGB(0, 5, 15070207);
810 img.setRGB(0, 6, 67156735);
811 img.setRGB(0, 7, 1996537334);
812 img.setRGB(0, 8, 1409335286);
813 img.setRGB(1, 0, 1996537334);
814 img.setRGB(1, 1, -1476346122);
815 img.setRGB(1, 2, 1996537334);
816 img.setRGB(1, 3, 50374655);
817 img.setRGB(1, 4, 15070207);
818 img.setRGB(1, 5, 67156735);
819 img.setRGB(1, 6, 1996537334);
820 img.setRGB(1, 7, -1476346122);
821 img.setRGB(1, 8, 1996537334);
822 img.setRGB(2, 0, 67156735);
823 img.setRGB(2, 1, 1996537334);
824 img.setRGB(2, 2, -1476346122);
825 img.setRGB(2, 3, 1996537334);
826 img.setRGB(2, 4, 151042815);
827 img.setRGB(2, 5, 1996537334);
828 img.setRGB(2, 6, -1476346122);
829 img.setRGB(2, 7, 1996537334);
830 img.setRGB(2, 8, 67156735);
831 img.setRGB(3, 0, 15070207);
832 img.setRGB(3, 1, 67156735);
833 img.setRGB(3, 2, 1996537334);
834 img.setRGB(3, 3, -1476346122);
835 img.setRGB(3, 4, -1660895755);
836 img.setRGB(3, 5, -1476346122);
837 img.setRGB(3, 6, 1996537334);
838 img.setRGB(3, 7, 67156735);
839 img.setRGB(3, 8, 15070207);
840 img.setRGB(4, 0, 15070207);
841 img.setRGB(4, 1, 15070207);
842 img.setRGB(4, 2, 67156735);
843 img.setRGB(4, 3, 1996537334);
844 img.setRGB(4, 4, -1476346122);
845 img.setRGB(4, 5, 1996537334);
846 img.setRGB(4, 6, 67156735);
847 img.setRGB(4, 7, 15070207);
848 img.setRGB(4, 8, 15070207);
849 img.setRGB(5, 0, 15070207);
850 img.setRGB(5, 1, 15070207);
851 img.setRGB(5, 2, 15070207);
852 img.setRGB(5, 3, 67156735);
853 img.setRGB(5, 4, 1375779830);
854 img.setRGB(5, 5, 67156735);
855 img.setRGB(5, 6, 15070207);
856 img.setRGB(5, 7, 15070207);
857 img.setRGB(5, 8, 15070207);
858 return img;
859 }
860 }