Ticket #3500: new_jmultiline.patch

File new_jmultiline.patch, 9.1 KB (added by xeen, 17 years ago)

Well, apparently I did work on it. This is a quick hack after I read a tip on Sun's bug tracker. Labels do support line breaking, but one must set the preferred width. The new jmutliline class derives this value automatically from the parent window. The patch is not well tested, there might be problems. Requires ExtendedDialog to not use a JScrollpane because it will display scrollbars even when not needed (they arent taken into account, might be fixable though). I didn't check if the max-width function actually works. Didn't check for other consumers, but it should have the same function names. Maybe I'll fix issues in my tree if I find them, but guarantees that this ever becomes a good patch.

  • src/org/openstreetmap/josm/gui/JMultilineLabel.java

     
    11// License: GPL. For details, see LICENSE file.
    22
    3 // This class was taken from
    4 // http://forum.java.sun.com/thread.jspa?threadID=459705&messageID=2104021
    5 // - Removed hardcoded margin
    6 // -  Added constructor
    7 
    83package org.openstreetmap.josm.gui;
    94
     5import java.awt.Component;
    106import java.awt.Dimension;
    11 import java.awt.Graphics;
    12 import java.awt.Graphics2D;
    13 import java.awt.Insets;
    14 import java.awt.font.FontRenderContext;
    15 import java.awt.font.LineBreakMeasurer;
    16 import java.awt.font.TextAttribute;
    17 import java.awt.font.TextLayout;
    18 import java.text.AttributedCharacterIterator;
    19 import java.text.AttributedString;
     7import java.awt.Window;
    208
    21 import javax.swing.JComponent;
     9import javax.swing.JLabel;
    2210
    23 public class JMultilineLabel extends JComponent {
    24     private String text;
    25     private int maxWidth = Integer.MAX_VALUE;
    26     private boolean justify;
    27     private final FontRenderContext frc = new FontRenderContext(null, false, false);
     11public class JMultilineLabel extends JLabel {
     12    private Component parent = null;
    2813
    29     public JMultilineLabel(String description) {
     14    /**
     15     * Constructs a normal label that wraps its text according to the limits of
     16     * the containing <b>window</b>.
     17     * @param text
     18     */
     19    public JMultilineLabel(String text)
     20    {
    3021        super();
    31         setText(description);
    32     }
    33 
    34     private void morph() {
    35         revalidate();
    36         repaint();
    37     }
    38 
    39     public String getText() {
    40         return text;
    41     }
    42 
    43     public void setText(String text) {
    44         String old = this.text;
    45         this.text = text;
    46         firePropertyChange("text", old, this.text);
    47         if ((old == null) ? text!=null : !old.equals(text))
    48             morph();
    49     }
    50 
    51     public int getMaxWidth() {
    52         return maxWidth;
    53     }
    54 
    55     public void setMaxWidth(int maxWidth) {
    56         if (maxWidth <= 0)
    57             throw new IllegalArgumentException();
    58         int old = this.maxWidth;
    59         this.maxWidth = maxWidth;
    60         firePropertyChange("maxWidth", old, this.maxWidth);
    61         if (old !=  this.maxWidth)
    62             morph();
     22        text = text.trim().replaceAll("\n", "<br>");
     23        if(!text.startsWith("<html>")) {
     24            text = "<html>" + text + "</html>";
     25        }
     26        super.setText(text);
    6327    }
    6428
    65     public boolean isJustified() {
    66         return justify;
     29    /**
     30     * Set the maximum width. This is a convenience function for using the
     31     * normal setMaximumSize()
     32     * @param width
     33     */
     34    public void setMaxWidth(int width) {
     35        Dimension superDim = super.getMaximumSize();
     36        super.setMaximumSize(new Dimension(width, superDim.height));
    6737    }
    6838
    69     public void setJustified(boolean justify) {
    70         boolean old = this.justify;
    71         this.justify = justify;
    72         firePropertyChange("justified", old, this.justify);
    73         if (old != this.justify)
    74             repaint();
    75     }
     39    /**
     40     * This overridden getPrefferedSize will try to find its parent window and
     41     * derive its width from that. If none can be found, it will behave exactly
     42     * as a normal 1-line label.
     43     */
     44    @Override
     45    public Dimension getPreferredSize()
     46    {
     47        Dimension superPreferred=super.getPreferredSize();
    7648
    77     public Dimension getPreferredSize() {
    78         return paintOrGetSize(null, getMaxWidth());
    79     }
     49        // Find outer Frame
     50        if(this.parent == null) {
     51            this.parent = this.getParent();
     52            while(this.parent.getParent() != null && !(this.parent instanceof Window)) {
     53                this.parent = this.parent.getParent();
     54            }
     55        }
    8056
    81     public Dimension getMinimumSize() {
    82         return getPreferredSize();
    83     }
     57        if(this.parent == null)
     58            return superPreferred;
    8459
    85     protected void paintComponent(Graphics g) {
    86         super.paintComponent(g);
    87         paintOrGetSize((Graphics2D)g, getWidth());
    88     }
     60        int width;
     61        // We can only get the bounds if the window is visible, otherwise they
     62        // will be 0. We cannot poll getPreferredSize because this will lead to
     63        // infinite recursion as this function will be called again
     64        if(parent.isVisible()) {
     65            // This is different from getWidth() as it returns the actual outer
     66            // size while getWidth() returns the size as required by the
     67            // contained components
     68            width = parent.getBounds().width;
     69        } else {
     70            width = parent.getMaximumSize().width;
     71        }
    8972
    90     private Dimension paintOrGetSize(Graphics2D g, int width) {
    91         Insets insets = getInsets();
    92         width -= insets.left + insets.right;
    93         float w = insets.left + insets.right;
    94         float x = insets.left, y=insets.top;
     73        width -= getInsets().left + getInsets().right;
    9574
    96         if (width > 0 && text != null && text.length() > 0) {
    97             String[] lines = getText().split("\n");
    98             for(String line : lines) {
    99                 // Insert a space so new lines get rendered
    100                 if(line.length() == 0) line = " ";
    101                 AttributedString as = new AttributedString(line);
    102                 as.addAttribute(TextAttribute.FONT, getFont());
    103                 AttributedCharacterIterator aci = as.getIterator();
    104                 LineBreakMeasurer lbm = new LineBreakMeasurer(aci, frc);
    105                 float max = 0;
    106                 while (lbm.getPosition() < aci.getEndIndex()) {
    107                     TextLayout textLayout = lbm.nextLayout(width);
    108                     if (g != null && isJustified() && textLayout.getVisibleAdvance() > 0.80 * width)
    109                         textLayout = textLayout.getJustifiedLayout(width);
    110                     if (g != null)
    111                         textLayout.draw(g, x, y + textLayout.getAscent());
    112                     y += textLayout.getDescent() + textLayout.getLeading() + textLayout.getAscent();
    113                     max = Math.max(max, textLayout.getVisibleAdvance());
    114                 }
    115                 w = Math.max(max, w);
    116             }
    117         }
    118         return new Dimension((int)Math.ceil(w), (int)Math.ceil(y) + insets.bottom);
     75        // Make the label not larger than required
     76        return new Dimension(
     77                Math.min(width, superPreferred.width),
     78                superPreferred.height
     79        );
    11980    }
    12081}
  • src/org/openstreetmap/josm/gui/ExtendedDialog.java

     
    134134     * @param message The text that should be shown to the user
    135135     */
    136136    public void setContent(String message) {
    137         setContent(string2label(message), true);
     137        setContent(string2label(message), false);
    138138    }
    139139
    140140    /**
     
    291291    public void setVisible(boolean visible) {
    292292        if (visible) {
    293293            repaint();
    294         }
    295 
    296         // Ensure all required variables are available
    297         if(!rememberSizePref.isEmpty() && defaultWindowGeometry != null) {
    298             if(visible) {
    299                 new WindowGeometry(rememberSizePref,
    300                         defaultWindowGeometry).apply(this);
    301             } else {
    302                 new WindowGeometry(this).remember(rememberSizePref);
     294            if(!rememberSizePref.isEmpty() && defaultWindowGeometry != null) {
     295                new WindowGeometry(rememberSizePref, defaultWindowGeometry).apply(this);
    303296            }
    304297        }
    305298        super.setVisible(visible);
     
    310303     * Set the pref to <code>null</code> or to an empty string to disable again.
    311304     * By default, it's disabled.
    312305     *
    313      * Note: If you want to set the width of this dialog directly use the usual
    314      * setSize, setPreferredSize, setMaxSize, setMinSize
    315      *
    316306     * @param pref  The preference to save the dimension to
    317307     * @param wg    The default window geometry that should be used if no
    318308     *              existing preference is found (only takes effect if
    319309     *              <code>pref</code> is not null or empty
    320310     *
     311     * Note: If you want to set the width of this dialog directly use the usual
     312     * setSize, setPreferredSize, setMaxSize, setMinSize
    321313     */
    322314    public void setRememberWindowGeometry(String pref, WindowGeometry wg) {
    323315        rememberSizePref = pref == null ? "" : pref;
     
    387379     */
    388380    private static JMultilineLabel string2label(String msg) {
    389381        JMultilineLabel lbl = new JMultilineLabel(msg);
    390         // Make it not wider than 2/3 of the screen
     382        // Make it not wider than 1/2 of the screen
    391383        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
    392         lbl.setMaxWidth(Math.round(screenSize.width*2/3));
     384        lbl.setMaxWidth(Math.round(screenSize.width*1/2));
    393385        return lbl;
    394386    }
    395387}
     388 No newline at end of file