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 }