1 package com.sun.java.swing.plaf.windows;
2
3 import java.awt.CardLayout;
4 import java.awt.Color;
5 import static java.awt.Color.WHITE;
6 import java.awt.Component;
7 import java.awt.Container;
8 import java.awt.Dimension;
9 import java.awt.Graphics;
10 import java.awt.Image;
11 import java.awt.Insets;
12 import java.awt.LayoutManager;
13 import java.awt.SystemColor;
14 import java.awt.Toolkit;
15 import java.awt.event.ActionEvent;
16 import java.awt.event.ActionListener;
17 import java.awt.event.AdjustmentEvent;
18 import java.awt.event.AdjustmentListener;
19 import java.awt.event.FocusAdapter;
20 import java.awt.event.FocusEvent;
21 import java.awt.event.FocusListener;
22 import java.awt.event.KeyAdapter;
23 import java.awt.event.KeyEvent;
24 import static java.awt.event.KeyEvent.VK_ENTER;
25 import static java.awt.event.KeyEvent.VK_ESCAPE;
26 import java.awt.event.KeyListener;
27 import java.awt.event.MouseAdapter;
28 import java.awt.event.MouseEvent;
29 import java.awt.event.MouseWheelEvent;
30 import java.awt.event.MouseWheelListener;
31 import java.awt.image.BufferedImage;
32 import java.io.File;
33 import java.io.UncheckedIOException;
34 import static java.lang.Boolean.TRUE;
35 import java.util.ArrayList;
36 import java.util.List;
37 import javax.swing.Icon;
38 import javax.swing.ImageIcon;
39 import javax.swing.JButton;
40 import javax.swing.JFileChooser;
41 import static javax.swing.JFileChooser.DIRECTORY_CHANGED_PROPERTY;
42 import javax.swing.JLabel;
43 import javax.swing.JMenuItem;
44 import javax.swing.JOptionPane;
45 import javax.swing.JPanel;
46 import javax.swing.JPopupMenu;
47 import javax.swing.JScrollBar;
48 import javax.swing.JTextField;
49 import javax.swing.JToolBar;
50 import javax.swing.border.Border;
51 import javax.swing.border.CompoundBorder;
52 import javax.swing.border.EmptyBorder;
53 import javax.swing.border.LineBorder;
54 import javax.swing.border.MatteBorder;
55 import javax.swing.filechooser.FileSystemView;
56 import sun.awt.shell.ShellFolder;
57
58 class WindowsAddressBar extends JPanel {
59
60 private final JFileChooser fileChooser;
61 private final CardLayout cardLayout = new CardLayout();
62 private final PathEditorField textField = new PathEditorField();
63 private final WindowsPathField pathField;
64 private final Border originalTextFieldBorder = textField.getBorder();
65
66 public WindowsAddressBar(JFileChooser fileChooser) {
67 this.fileChooser = fileChooser;
68 this.pathField = new WindowsPathField(fileChooser, this);
69
70 setLayout(cardLayout);
71 add(textField, "text");
72 add(pathField, "path");
73 cardLayout.show(this, "path");
74
75 if (Toolkit.getDefaultToolkit().
76 getDesktopProperty("win.xpstyle.themeActive") != TRUE) {
77 textField.setBackground(SystemColor.control);
78 textField.setOpaque(true);
79
80 pathField.setBackground(SystemColor.control);
81 pathField.setOpaque(true);
82 }
83 }
84
85 private void showTextField() {
86 textField.refresh();
87 cardLayout.show(this, "text");
88 revalidate();
89 textField.requestFocus();
90 textField.selectAll();
91 }
92
93 private void showPathField() {
94 cardLayout.show(this, "path");
95 revalidate();
96 }
97
98 private class PathEditorField extends JTextField implements FocusListener, KeyListener {
99
100 public PathEditorField() {
101 }
102
103 public void refresh() {
104 textField.setText(fileChooser.getCurrentDirectory().getPath());
105
106 Icon icon = pathField.getCurrentIcon();
107 setBorder(new CompoundBorder(
108 originalTextFieldBorder, new CompoundBorder(
109 new EmptyBorder(1, 2, 0, 0), new CompoundBorder(
110 new MatteBorder(0, icon.getIconWidth(), 0, 0, icon),
111 new EmptyBorder(0, 9, 0, 0)
112 )
113 )
114 ));
115 }
116
117 @Override
118 public void keyPressed(KeyEvent e) {
119 switch (e.getKeyCode()) {
120 case VK_ENTER:
121 String path = textField.getText();
122 File file;
123 try {
124 file = (File) ShellFolder.get("parseDisplayName " + path);
125 } catch (UncheckedIOException ex) {
126 cantParseDisplayName(path);
127 break;
128 }
129 fileChooser.setCurrentDirectory(file);
130 case VK_ESCAPE:
131 e.consume();
132 showPathField();
133 break;
134 }
135 }
136
137 private void cantParseDisplayName(String path) {
138 ((Runnable) Toolkit.getDefaultToolkit().
139 getDesktopProperty("win.sound.exclamation")).run();
140 JOptionPane.showMessageDialog(fileChooser, path + "\n"
141 + "The specified path does not exist. \n\n"
142 + "Check the path, and then try again. ",
143 path, JOptionPane.ERROR_MESSAGE);
144 textField.selectAll();
145 textField.requestFocusInWindow();
146 }
147
148 @Override
149 public void focusLost(FocusEvent e) {
150 showPathField();
151 }
152
153 @Override
154 public void focusGained(FocusEvent e) {
155 }
156
157 @Override
158 public void keyTyped(KeyEvent e) {
159 throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
160 }
161
162 @Override
163 public void keyReleased(KeyEvent e) {
164 throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
165 }
166
167 }
168
169 private class WindowsPathField extends JToolBar {
170
171 private final JFileChooser fileChooser;
172 private final FileSystemView view;
173 private RootElementLabel rootElementLabel;
174
175 public WindowsPathField(JFileChooser fileChooser, WindowsAddressBar addressBar) {
176 this.fileChooser = fileChooser;
177 this.view = fileChooser.getFileSystemView();
178
179 fileChooser.addPropertyChangeListener(
180 DIRECTORY_CHANGED_PROPERTY, (p) -> refresh());
181 setFloatable(false);
182 setBackground(WHITE);
183 setBorder(new CompoundBorder(
184 new LineBorder(new Color(217, 217, 217)),
185 new EmptyBorder(0, 4, 0, 2)
186 ));
187 putClientProperty("XPStyle.subAppName", "Header");
188 putClientProperty("WindowsButtonUI.animateStateChange", TRUE);
189
190 addMouseListener(new MouseAdapter() {
191 @Override
192 public void mouseReleased(MouseEvent e) {
193 addressBar.showTextField();
194 }
195
196 });
197 }
198
199 public Icon getCurrentIcon() {
200 return rootElementLabel.getIcon();
201 }
202
203 private void refresh() {
204 while (getComponentCount() > 0) {
205 remove(0);
206 }
207
208 File dir = fileChooser.getCurrentDirectory();
209 List<File> path = new ArrayList<>();
210 do {
211 path.add(0, dir);
212 } while ((dir = dir.getParentFile()) != null);
213
214 add(rootElementLabel = new RootElementLabel(path));
215 add(new RightArrowButton(path.get(0)));
216 for (File pathElement : path.subList(1, path.size())) {
217 add(new PathElementButton(pathElement));
218 add(new RightArrowButton(pathElement));
219 }
220
221 revalidate();
222 repaint();
223
224 System.out.println("refreshed");
225 }
226
227 private final class RootElementLabel extends JLabel {
228
229 private RootElementLabel(List<File> path) {
230 File folder = path.get(path.size() - 1);
231 if (path.size() == 1) {
232 setText(view.getSystemDisplayName(folder));
233 }
234 setIcon(view.getSystemIcon(folder));
235 setBackground(WHITE);
236 }
237 }
238
239 private final class PathElementButton extends JButton implements ActionListener {
240
241 private final File folder;
242
243 private PathElementButton(File dir) {
244 this.folder = dir;
245
246 setText(view.getSystemDisplayName(dir));
247 addActionListener(this);
248 setOpaque(false);
249 setBorder(new EmptyBorder(3, 5, 3, 5));
250 }
251
252 @Override
253 public void actionPerformed(ActionEvent e) {
254 fileChooser.setCurrentDirectory(folder);
255 }
256 }
257
258 private class RightArrowButton extends JButton implements ActionListener {
259
260 private final File folder;
261
262 public RightArrowButton(File folder) {
263 setIcon(new ImageIcon(initRightArrowImage()));
264 setBorder(new EmptyBorder(3, 5, 3, 5));
265 setOpaque(false);
266 addActionListener(this);
267 this.folder = folder;
268 }
269
270 @Override
271 public void actionPerformed(ActionEvent e) {
272 JPopupMenu popupMenu = new ScrollablePopupMenu();
273 File[] files = view.getFiles(folder, false);
274 for (File file : files) {
275 if (!view.isTraversable(file)) {
276 continue;
277 }
278
279 JMenuItem menuItem = new JMenuItem();
280 menuItem.setText(view.getSystemDisplayName(file));
281 menuItem.addActionListener(evt -> {
282 fileChooser.setCurrentDirectory(file);
283 });
284 popupMenu.add(menuItem);
285 }
286
287 popupMenu.show(this, 0, getHeight());
288 }
289
290 }
291
292 }
293
294 private static class ScrollablePopupMenu extends JPopupMenu {
295
296 private static final int MAXIMUM_VISIBLE_ROWS = 18;
297
298 public ScrollablePopupMenu() {
299 setLayout(new ScrollPopupMenuLayout());
300
301 super.add(getScrollBar());
302 addMouseWheelListener(new MouseWheelListener() {
303 @Override
304 public void mouseWheelMoved(MouseWheelEvent event) {
305 JScrollBar scrollBar = getScrollBar();
306 int amount = (event.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL)
307 ? event.getUnitsToScroll() * scrollBar.getUnitIncrement()
308 : (event.getWheelRotation() < 0 ? -1 : 1) * scrollBar.getBlockIncrement();
309
310 scrollBar.setValue(scrollBar.getValue() + amount);
311 event.consume();
312 }
313 });
314 }
315
316 private JScrollBar popupScrollBar;
317
318 private JScrollBar getScrollBar() {
319 if (popupScrollBar == null) {
320 popupScrollBar = new JScrollBar(JScrollBar.VERTICAL);
321 popupScrollBar.addAdjustmentListener(new AdjustmentListener() {
322 @Override
323 public void adjustmentValueChanged(AdjustmentEvent e) {
324 doLayout();
325 repaint();
326 }
327 });
328
329 popupScrollBar.setVisible(false);
330 }
331
332 return popupScrollBar;
333 }
334
335 public void paintChildren(Graphics g) {
336 Insets insets = getInsets();
337 g.clipRect(insets.left, insets.top, getWidth(), getHeight() - insets.top - insets.bottom);
338 super.paintChildren(g);
339 }
340
341 protected void addImpl(Component comp, Object constraints, int index) {
342 super.addImpl(comp, constraints, index);
343
344 if (MAXIMUM_VISIBLE_ROWS < getComponentCount() - 1) {
345 getScrollBar().setVisible(true);
346 }
347 }
348
349 public void remove(int index) {
350 // can't remove the scrollbar
351 ++index;
352
353 super.remove(index);
354
355 if (MAXIMUM_VISIBLE_ROWS >= getComponentCount() - 1) {
356 getScrollBar().setVisible(false);
357 }
358 }
359
360 public void show(Component invoker, int x, int y) {
361 JScrollBar scrollBar = getScrollBar();
362 if (scrollBar.isVisible()) {
363 int extent = 0;
364 int max = 0;
365 int i = 0;
366 int unit = -1;
367 int width = 0;
368 for (Component comp : getComponents()) {
369 if (!(comp instanceof JScrollBar)) {
370 Dimension preferredSize = comp.getPreferredSize();
371 width = Math.max(width, preferredSize.width);
372 if (unit < 0) {
373 unit = preferredSize.height;
374 }
375 if (i++ < MAXIMUM_VISIBLE_ROWS) {
376 extent += preferredSize.height;
377 }
378 max += preferredSize.height;
379 }
380 }
381
382 Insets insets = getInsets();
383 int widthMargin = insets.left + insets.right;
384 int heightMargin = insets.top + insets.bottom;
385 scrollBar.setUnitIncrement(unit);
386 scrollBar.setBlockIncrement(extent);
387 scrollBar.setValues(0, heightMargin + extent, 0, heightMargin + max);
388
389 width += scrollBar.getPreferredSize().width + widthMargin;
390 int height = heightMargin + extent;
391
392 setPopupSize(new Dimension(width, height));
393 }
394
395 super.show(invoker, x, y);
396 }
397
398 private static class ScrollPopupMenuLayout implements LayoutManager {
399
400 @Override
401 public void addLayoutComponent(String name, Component comp) {
402 }
403
404 @Override
405 public void removeLayoutComponent(Component comp) {
406 }
407
408 @Override
409 public Dimension preferredLayoutSize(Container parent) {
410 int visibleAmount = Integer.MAX_VALUE;
411 Dimension dim = new Dimension();
412 for (Component comp : parent.getComponents()) {
413 if (comp.isVisible()) {
414 if (comp instanceof JScrollBar) {
415 JScrollBar scrollBar = (JScrollBar) comp;
416 visibleAmount = scrollBar.getVisibleAmount();
417 } else {
418 Dimension pref = comp.getPreferredSize();
419 dim.width = Math.max(dim.width, pref.width);
420 dim.height += pref.height;
421 }
422 }
423 }
424
425 Insets insets = parent.getInsets();
426 dim.height = Math.min(dim.height + insets.top + insets.bottom, visibleAmount);
427
428 return dim;
429 }
430
431 @Override
432 public Dimension minimumLayoutSize(Container parent) {
433 int visibleAmount = Integer.MAX_VALUE;
434 Dimension dim = new Dimension();
435 for (Component comp : parent.getComponents()) {
436 if (comp.isVisible()) {
437 if (comp instanceof JScrollBar) {
438 JScrollBar scrollBar = (JScrollBar) comp;
439 visibleAmount = scrollBar.getVisibleAmount();
440 } else {
441 Dimension min = comp.getMinimumSize();
442 dim.width = Math.max(dim.width, min.width);
443 dim.height += min.height;
444 }
445 }
446 }
447
448 Insets insets = parent.getInsets();
449 dim.height = Math.min(dim.height + insets.top + insets.bottom, visibleAmount);
450
451 return dim;
452 }
453
454 @Override
455 public void layoutContainer(Container parent) {
456 Insets insets = parent.getInsets();
457
458 int width = parent.getWidth() - insets.left - insets.right;
459 int height = parent.getHeight() - insets.top - insets.bottom;
460
461 int x = insets.left;
462 int y = insets.top;
463 int position = 0;
464
465 for (Component comp : parent.getComponents()) {
466 if ((comp instanceof JScrollBar) && comp.isVisible()) {
467 JScrollBar scrollBar = (JScrollBar) comp;
468 Dimension dim = scrollBar.getPreferredSize();
469 scrollBar.setBounds(x + width - dim.width, y, dim.width, height);
470 width -= dim.width;
471 position = scrollBar.getValue();
472 }
473 }
474
475 y -= position;
476 for (Component comp : parent.getComponents()) {
477 if (!(comp instanceof JScrollBar) && comp.isVisible()) {
478 Dimension pref = comp.getPreferredSize();
479 comp.setBounds(x, y, width, pref.height);
480 y += pref.height;
481 }
482 }
483 }
484 }
485 }
486
487 private static Image initRightArrowImage() {
488 BufferedImage img = new BufferedImage(5, 7, 6);
489 img.setRGB(0, 0, 150994944);
490 img.setRGB(0, 1, 1191182336);
491 img.setRGB(0, 2, 50331648);
492 img.setRGB(0, 3, 16777215);
493 img.setRGB(0, 4, 50331648);
494 img.setRGB(0, 5, 1191182336);
495 img.setRGB(0, 6, 150994944);
496 img.setRGB(1, 0, 1174405120);
497 img.setRGB(1, 1, 2130706432);
498 img.setRGB(1, 2, 1526726656);
499 img.setRGB(1, 3, 100663296);
500 img.setRGB(1, 4, 1526726656);
501 img.setRGB(1, 5, 2130706432);
502 img.setRGB(1, 6, 1174405120);
503 img.setRGB(2, 0, 50331648);
504 img.setRGB(2, 1, 1526726656);
505 img.setRGB(2, 2, 2130706432);
506 img.setRGB(2, 3, 1996488704);
507 img.setRGB(2, 4, 2130706432);
508 img.setRGB(2, 5, 1526726656);
509 img.setRGB(2, 6, 50331648);
510 img.setRGB(3, 1, 50331648);
511 img.setRGB(3, 2, 1526726656);
512 img.setRGB(3, 3, 2130706432);
513 img.setRGB(3, 4, 1526726656);
514 img.setRGB(3, 5, 50331648);
515 img.setRGB(4, 2, 50331648);
516 img.setRGB(4, 3, 1040187392);
517 img.setRGB(4, 4, 50331648);
518 return img;
519 }
520
521 }