3

I have a JTextField textField and I have an arbitrary String string

I want the text field to display the string's end but cut off what it can't fit and use "..." in place of it. For example, if the string is "Hello How Are You Doing" then the text field may look something like

...re You Doing

Given textfield and string, how can I do this?

UPDATE: the reason I want to do this is because it's a file chooser so I want to display the end of the file with priority. Example, if the user selects to save at /Users/Name/Documents/Folder1/Folder2/Folder3/file.txt then I want to display the end and the rest that can't fit should be replaced with "..."

Here is where it's done:

JFileChooser chooser = new JFileChooser();
int response = chooser.showSaveDialog(this);
if(response == JFileChooser.APPROVE_OPTION) {
    File file = chooser.getSelectedFile();
    String string = file.getAbsolutePath();

    fileTextField.setText(/* here's where the code would go */);
}
CodeGuy
  • 28,427
  • 76
  • 200
  • 317

3 Answers3

5

..it's a file chooser so I want to display the end of the file with priority.

I suggest either this as an alternative, or drop the tool-tip (path) in after the file name.

FileListName

import java.awt.*;
import javax.swing.*;
import javax.swing.filechooser.FileSystemView;
import java.io.File;

class FileListName {

    final class FileListCellRenderer extends DefaultListCellRenderer {

        private FileSystemView fsv = FileSystemView.getFileSystemView();

        @Override
        public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
            Component c = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
            JLabel l = (JLabel) c;
            File f = (File) value;
            l.setText(f.getName());
            l.setIcon(fsv.getSystemIcon(f));
            l.setToolTipText(f.getAbsolutePath());

            return l;
        }
    }

    public FileListName() {
        JFileChooser jfc = new JFileChooser();
        jfc.showOpenDialog(null);
        if (jfc.getSelectedFile() != null) {
            File[] f = {jfc.getSelectedFile()};
            JList list = new JList(f);
            list.setVisibleRowCount(1);
            list.setCellRenderer(new FileListCellRenderer());
            JOptionPane.showMessageDialog(null, list);
        }
    }

    public static void main(String[] args) {
        Runnable r = new Runnable() {

            @Override
            public void run() {
                try {
                    // Provides better icons from the FSV.
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (Exception useDefault) {
                }
                new FileListName();
            }
        };
        // Swing GUIs should be created and updated on the EDT
        // http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html
        SwingUtilities.invokeLater(r);
    }
}
Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
3

The problem you will face is the fact that String#length doesn't match pixel width. You need to take into consideration the current font, the width of the component, the icon, icon spacing, insets, screen DPI.... :P

enter image description here

public class TrimPath {

    public static void main(String[] args) {
        new TrimPath();
    }

    public TrimPath() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException ex) {
                } catch (InstantiationException ex) {
                } catch (IllegalAccessException ex) {
                } catch (UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());

                File path = new File("C:\\Users\\Default\\AppData\\Local\\Microsoft\\Windows\\GameExplorer");
                TrimmedLabel label = new TrimmedLabel(path.getPath());
                label.setIcon(FileSystemView.getFileSystemView().getSystemIcon(path));

                frame.add(label);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TrimmedLabel extends JLabel {

        private String masterText;

        public TrimmedLabel(String text) {
            super(text);
        }

        @Override
        public void setText(String text) {
            if (masterText == null ? text != null : !masterText.equals(text)) {
                masterText = text;
                super.setText(text);
            }
        }

        @Override
        public String getText() {

            String text = getMasterText();

            if (text != null && text.length() > 0) {
                int width = getWidth();
                Icon icon = getIcon();
                if (icon != null) {
                    width -= (icon.getIconWidth() + getIconTextGap());
                }
                FontMetrics fm = getFontMetrics(getFont());
                if (width > 0 && fm != null) {
                    int strWidth = fm.stringWidth(text);
                    if (strWidth > width) {
                        StringBuilder sb = new StringBuilder(text);
                        String prefix = "...";
                        while (fm.stringWidth(prefix + sb.toString()) > width) {
                            sb.delete(0, 1);
                        }
                        text = prefix + sb.toString();
                    }
                }
            }
            return text;
        }

        public String getMasterText() {
            return masterText;
        }
    }
}
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • 1
    The condition `if (width > 0 && fm != null)` should also include a non-null test on `text`: `if (width > 0 && fm != null && text != null)` otherwise you will have a NPE on the next line. – Guillaume Polet Jan 07 '13 at 08:33
  • @GuillaumePolet Fair point, I've added in a check for `null` and empty `String`, cheers – MadProgrammer Jan 07 '13 at 08:49
0

can be done by setting your own Document

(this is quick'n'dirty so test lots of times)

final int CHAR_DISPLAY_MAX = 10;
final int REPLACE_NUMBER = 3;
final JTextField tf = new JTextField(7);
Document doc = new PlainDocument(){
  public void insertString(int offs, String str, AttributeSet a) throws BadLocationException{
    if (this.getLength() + str.length() > CHAR_DISPLAY_MAX)
    {
      String oldText = tf.getText();
      String newText = oldText+str;
      tf.setText("..."+newText.substring(newText.length() - (CHAR_DISPLAY_MAX-REPLACE_NUMBER)));
      return;
    }
    super.insertString(offs, str, a);
  }
};
tf.setDocument(doc);
Michael Dunn
  • 818
  • 5
  • 3
  • no, that's certainly not what you want to do. The document is-a _model_ and as such doesn't (and must not) know about the view state nor change it ... – kleopatra Jan 07 '13 at 10:59