VAqua.src.org.violetlib.aqua.fc.FilePreview Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of vaqua Show documentation
Show all versions of vaqua Show documentation
An improved native Swing look and feel for macOS
The newest version!
/*
* Copyright (c) 2009-2010 Werner Randelshofer, Switzerland.
* Copyright (c) 2014-2024 Alan Snyder.
* All rights reserved.
*
* You may not use, copy or modify this file, except in compliance with the
* license agreement you entered into with Werner Randelshofer.
* For details see accompanying license terms.
*/
package org.violetlib.aqua.fc;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.File;
import java.text.DateFormat;
import java.text.MessageFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Locale;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.filechooser.FileSystemView;
import javax.swing.plaf.basic.BasicHTML;
import javax.swing.table.*;
import javax.swing.tree.TreePath;
import org.violetlib.aqua.*;
/**
* The FilePreview is used to render the preview column in the file chooser browser view.
*/
public class FilePreview extends JComponent implements BrowserPreviewRenderer {
private JFileChooser fileChooser;
private FileInfo info;
private NameView nameView;
private JLabel typeSizeView;
private JTable attributeView;
private Font labelFont;
private Font valueFont;
private Font typeSizeFont;
private String labelDelimiter;
private TableColumn nameColumn;
private TableColumn valueColumn;
private SimpleTableCellRenderer nameRenderer;
private SimpleTableCellRenderer valueRenderer;
private FilePreviewView view;
private JPanel viewHolder;
public FilePreview(JFileChooser fileChooser)
throws UnsupportedOperationException {
this.fileChooser = fileChooser;
int version = OSXSystemProperties.OSVersion;
int minWidth = 210;
int prefWidth = version >= 1014 ? 240 : minWidth;
int minHeight = 335;
setMinimumSize(new Dimension(minWidth, minHeight));
setPreferredSize(new Dimension(prefWidth, minHeight));
viewHolder = new JPanel();
viewHolder.setMinimumSize(new Dimension(128, 128));
viewHolder.setPreferredSize(new Dimension(128, 128));
view = new FilePreviewView();
view.track(viewHolder);
setBorder(BorderFactory.createEmptyBorder(3, 4, 4, 4));
setLayout(new BorderLayout());
labelFont = UIManager.getFont("FileChooser.previewLabelFont");
valueFont = UIManager.getFont("FileChooser.previewValueFont");
typeSizeFont = UIManager.getFont("FileChooser.previewTypeSizeFont");
labelDelimiter = UIManager.getString("FileChooser.previewLabelDelimiter");
if (labelDelimiter == null) {
labelDelimiter = "";
}
int columnSeparation = 5;
{
TableColumnModel cm = new DefaultTableColumnModel();
{
nameColumn = new TableColumn();
nameRenderer = new SimpleTableCellRenderer(labelFont);
nameRenderer.setHorizontalAlignment(SwingConstants.RIGHT);
Insets borderMargin = new Insets(0, 0, 0, 0);
nameRenderer.putClientProperty("Quaqua.Component.visualMargin", borderMargin);
nameColumn.setCellRenderer(nameRenderer);
nameColumn.setModelIndex(0);
cm.addColumn(nameColumn);
}
{
valueColumn = new TableColumn();
valueRenderer = new SimpleTableCellRenderer(valueFont);
valueRenderer.setHorizontalAlignment(SwingConstants.LEFT);
valueColumn.setCellRenderer(valueRenderer);
valueColumn.setModelIndex(1);
cm.addColumn(valueColumn);
}
attributeView = new JTable(null, cm);
attributeView.setIntercellSpacing(new Dimension(columnSeparation, 0));
attributeView.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
attributeView.setRowSelectionAllowed(false);
attributeView.setFocusable(false);
attributeView.setOpaque(false);
attributeView.setAlignmentX(0.5f);
}
setOpaque(false);
add(viewHolder);
Box vb = new Box(BoxLayout.Y_AXIS);
add(vb, BorderLayout.SOUTH);
if (OSXSystemProperties.OSVersion < 1010) {
GrayLine b = new GrayLine();
b.setBorder(new EmptyBorder(5, 25, 5, 25));
vb.add(b);
} else {
nameView = new NameView();
nameView.setAlignmentX(0.5f);
if (OSXSystemProperties.OSVersion >= 1014) {
typeSizeView = new JLabel();
typeSizeView.setFont(typeSizeFont);
typeSizeView.setAlignmentX(0.5f);
} else {
valueRenderer.setRowZeroFont(typeSizeFont);
}
vb.add(Box.createVerticalStrut(5));
vb.add(nameView);
if (OSXSystemProperties.OSVersion < 1014) {
vb.add(Box.createVerticalStrut(20));
}
if (typeSizeView != null) {
vb.add(typeSizeView);
}
if (OSXSystemProperties.OSVersion >= 1014) {
vb.add(Box.createVerticalStrut(20));
}
}
vb.add(attributeView);
vb.add(Box.createVerticalStrut(40));
MouseListener mouseHandler = new MouseAdapter() {
public void mouseClicked(MouseEvent evt) {
if (evt.getClickCount() == 2) {
FilePreview.this.fileChooser.approveSelection();
}
}
};
addMouseListener(mouseHandler);
Component[] c = getComponents();
for (int i = 0; i < c.length; i++) {
c[i].addMouseListener(mouseHandler);
}
}
public void dispose() {
if (view != null) {
view.dispose();
view = null;
}
}
@Override
protected void paintComponent(Graphics g) {
AppearanceManager.ensureAppearance(this);
Color background = AquaColors.getBackground(this, "controlBackground");
Color labelForeground = AquaColors.getSystemColor(this, "secondaryLabel");
Color valueForeground = AquaColors.getSystemColor(this, "label");
nameRenderer.setColor(AquaColors.getOrdinaryColor(labelForeground));
valueRenderer.setColor(AquaColors.getOrdinaryColor(valueForeground));
if (typeSizeView != null) {
typeSizeView.setForeground(AquaColors.getOrdinaryColor(labelForeground));
} else if (OSXSystemProperties.OSVersion >= 1010) {
valueRenderer.setRowZeroColor(AquaColors.getOrdinaryColor(labelForeground));
}
// Avoid the magic eraser when displayed as a sheet
if (isOpaque()) {
g.setColor(background);
g.fillRect(0, 0, getWidth(), getHeight());
}
}
protected JProgressBar createImageLoadingIndicator() {
JProgressBar b = new JProgressBar();
b.setIndeterminate(true);
b.putClientProperty("JProgressBar.style", "circular");
return b;
}
private String toOSXPath(File file) {
StringBuffer buf = new StringBuffer();
FileSystemView fsv = AquaFileSystemView.getAquaFileSystemView();
if (file != null && file.isDirectory()) {
buf.append(':');
}
while (file != null) {
buf.insert(0, fileChooser.getName(file));
file = fsv.getParentDirectory(file);
if (file != null) {
buf.insert(0, ':');
}
}
return buf.toString();
}
public Component getPreviewRendererComponent(JBrowser browser, TreePath[] paths) {
if (paths.length != 1) {
setVisible(false);
return this;
}
Locale locale = Locale.getDefault();
NumberFormat nf = NumberFormat.getInstance(locale);
nf.setMaximumFractionDigits(1);
info = (FileInfo) paths[0].getLastPathComponent();
if (!info.isAcceptable()) {
setVisible(false);
return this;
}
setVisible(true);
File file = info.getFile();
String name = info.getUserName();
String kind = OSXFile.getKindString(file);
String size = getLengthString(info.getFileLength());
String modified = getModifiedString(file);
if (nameView != null) {
nameView.setText(name);
}
AttributeTableModel m = new AttributeTableModel();
if (OSXSystemProperties.OSVersion < 1010) {
m.add("name", name);
m.add("kind", kind);
if (size != null) {
m.add("size", size);
}
m.add("modified", modified);
Date lastUsedDate = OSXFile.getLastUsedDate(file);
if (lastUsedDate != null) {
m.add("lastUsed", getLastUsedString(lastUsedDate));
}
} else {
String s = kind;
if (size != null) {
s = s + " - " + size;
}
if (typeSizeView != null) {
typeSizeView.setText(s);
} else {
m.add("", s); // special font and text color for first row
}
m.add("modified", modified);
// TBD: in 10.14, it can take a long time to determine that the last used date is not
// available to an untrusted program.
if (OSXSystemProperties.OSVersion < 1014) {
Date lastUsedDate = OSXFile.getLastUsedDate(file);
if (lastUsedDate != null) {
m.add("lastUsed", getLastUsedString(lastUsedDate));
}
}
}
if (true) {
// The original of a symlink is not displayed by NSOpenPanel, but it seems useful.
if (info.isAlias()) {
File resolvedFile = info.lazyGetResolvedFile();
if (resolvedFile != null) {
m.add("original", toOSXPath(resolvedFile));
}
}
}
attributeView.setModel(m);
m.updatePreferredWidths();
attributeView.revalidate();
attributeView.repaint();
updatePreviewImage();
return this;
}
// Duplicated in ListViewImpl
protected String getLengthString(long fileLength) {
if (fileLength < 0) {
return null;
} else {
float scaledLength;
String label;
if (fileLength >= 1000000000l) {
label = "FileChooser.sizeGBytesOnly";
scaledLength = (float) fileLength / 1000000000l;
} else if (fileLength >= 1000000l) {
label = "FileChooser.sizeMBytesOnly";
scaledLength = (float) fileLength / 1000000l;
} else if (fileLength >= 1024) {
label = "FileChooser.sizeKBytesOnly";
scaledLength = (float) fileLength / 1000;
} else {
label = "FileChooser.sizeBytesOnly";
scaledLength = (float) fileLength;
}
String format = UIManager.getString(label);
if (format != null) {
return MessageFormat.format(format, scaledLength, fileLength);
} else {
return "" + fileLength;
}
}
}
protected String getModifiedString(File f) {
if (f != null) {
return DateFormat.getInstance().format(new Date(f.lastModified()));
} else {
return null; // UIManager.getString("FileChooser.modifiedUnknown");
}
}
protected String getLastUsedString(Date d) {
return DateFormat.getInstance().format(d);
}
private class AttributeTableModel extends AbstractTableModel {
private java.util.List names = new ArrayList();
private java.util.List values = new ArrayList();
private int nameWidth;
private int valueWidth;
public void add(String name, String value) {
if (value != null && !value.isEmpty()) {
String s = UIManager.getString("FileChooser." + name); // NOI18N
String actualName = (s != null ? s : name) + labelDelimiter;
names.add(actualName);
values.add(value);
nameWidth = Math.max(nameWidth, getTextWidth(actualName, labelFont, attributeView));
valueWidth = Math.max(valueWidth, getTextWidth(value, valueFont, attributeView));
}
}
public void updatePreferredWidths() {
int fudge = 15;
attributeView.getColumnModel().getColumn(0).setPreferredWidth(nameWidth + fudge);
attributeView.getColumnModel().getColumn(1).setPreferredWidth(valueWidth + fudge);
}
@Override
public int getRowCount() {
return names.size();
}
@Override
public int getColumnCount() {
return 2;
}
@Override
public String getColumnName(int columnIndex) {
if (columnIndex == 0) {
return "Attribute";
} else if (columnIndex == 1) {
return "Value";
} else {
return null;
}
}
@Override
public Class> getColumnClass(int columnIndex) {
return String.class;
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
if (columnIndex == 0) {
return names.get(rowIndex);
} else if (columnIndex == 1) {
return values.get(rowIndex);
} else {
return null;
}
}
}
private void updatePreviewImage() {
if (view != null) {
if (info != null) {
File file = info.lazyGetResolvedFile();
if (file != null) {
view.configure(file);
}
}
}
}
public static int getTextWidth(String s, Font f, JComponent c) {
Dimension size = getTextSize(s, f, c);
return size != null ? size.width : 0;
}
public static Dimension getTextSize(String s, Font f, JComponent c) {
if (f == null) {
f = c.getFont();
if (f == null) {
return null;
}
}
FontMetrics fm = null;
try {
// Workaround a Swing bug (probably old)
fm = c.getFontMetrics(f);
} catch (NullPointerException ex) {
}
if (fm == null) {
return null;
}
int w = fm.stringWidth(s);
int h = fm.getHeight();
return new Dimension(w, h);
}
private static class SimpleTableCellRenderer extends JLabel implements TableCellRenderer {
private Font f;
private Color fg;
private Font rowZeroFont;
private Color rowZeroColor;
public SimpleTableCellRenderer(Font f) {
this.f = f;
this.fg = Color.BLACK; // temporary, configured later
setOpaque(false);
}
public void setRowZeroFont(Font f) {
this.rowZeroFont = f;
}
public void setRowZeroColor(Color c) {
this.rowZeroColor = c;
}
public void setColor(Color c) {
this.fg = c;
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
setText((value == null) ? "" : value.toString());
setFont(row == 0 && rowZeroFont != null ? rowZeroFont : f);
setForeground(row == 0 && rowZeroColor != null ? rowZeroColor : fg);
return this;
}
/**
* Overridden for performance reasons.
* See the Implementation Note
* for more information.
*
* @since 1.5
*/
public void invalidate() {}
/**
* Overridden for performance reasons.
* See the Implementation Note
* for more information.
*/
public void validate() {}
/**
* Overridden for performance reasons.
* See the Implementation Note
* for more information.
*/
public void revalidate() {}
/**
* Overridden for performance reasons.
* See the Implementation Note
* for more information.
*/
public void repaint(long tm, int x, int y, int width, int height) {}
/**
* Overridden for performance reasons.
* See the Implementation Note
* for more information.
*/
public void repaint(Rectangle r) {}
/**
* Overridden for performance reasons.
* See the Implementation Note
* for more information.
*/
public void repaint() {}
/**
* Overridden for performance reasons.
* See the Implementation Note
* for more information.
*/
protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
// Strings get interned...
if (propertyName=="text"
|| propertyName == "labelFor"
|| propertyName == "displayedMnemonic"
|| ((propertyName == "font" || propertyName == "foreground")
&& oldValue != newValue
&& getClientProperty(BasicHTML.propertyKey) != null)) {
super.firePropertyChange(propertyName, oldValue, newValue);
}
}
/**
* Overridden for performance reasons.
* See the Implementation Note
* for more information.
*/
public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) {}
}
private static class GrayLine extends JComponent {
@Override
public Dimension getMinimumSize() {
Insets s = getInsets();
return new Dimension(0, s.top + s.bottom + 1);
}
@Override
public Dimension getPreferredSize() {
return getMinimumSize();
}
@Override
public Dimension getMaximumSize() {
Insets s = getInsets();
return new Dimension(100000, s.top + s.bottom + 1);
}
@Override
protected void paintComponent(Graphics g) {
if (isOpaque()) {
g.setColor(getBackground());
g.fillRect(0, 0, getWidth(), getHeight());
}
Insets s = getInsets();
g.setColor(new Color(217, 217, 217));
g.fillRect(s.left, s.top, getWidth() - s.left - s.right, 1);
}
}
private static class ScaledImageView extends JComponent {
private Image im;
public void setImage(Image im) {
this.im = im;
repaint();
}
@Override
protected void paintComponent(Graphics g) {
if (isOpaque()) {
g.setColor(getBackground());
g.fillRect(0, 0, getWidth(), getHeight());
}
if (im != null) {
Insets s = getInsets();
int left = s.left;
int top = s.top;
int cwidth = getWidth() - s.left - s.right;
int cheight = getHeight() - s.top - s.bottom;
float imwidth = im.getWidth(null);
float imheight = im.getHeight(null);
if (imwidth > 0 && imheight > 0) {
int size = Math.min(cwidth, cheight);
float scale = Math.min(cwidth/imwidth, cheight/imheight);
int extraLeft = (int) Math.max(0, (cwidth - imwidth*scale) / 2);
int extraTop = (int) Math.max(0, (cheight - imheight*scale) / 2);
g.drawImage(im, left + extraLeft, top + extraTop, size, size, null);
}
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy