United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
Bug ID: JDK-4134077 Metal,Window:If JInternalFrame's title text is long last must be ellipsis
JDK-4134077 : Metal,Window:If JInternalFrame's title text is long last must be ellipsis

Details
Type:
Bug
Submit Date:
1998-04-30
Status:
Closed
Updated Date:
2000-05-22
Project Name:
JDK
Resolved Date:
2000-04-19
Component:
client-libs
OS:
windows_nt,generic
Sub-Component:
javax.swing
CPU:
x86,generic
Priority:
P3
Resolution:
Fixed
Affected Versions:
1.2.0,1.3.0
Fixed Versions:
1.4.0 (beta)

Related Reports
Duplicate:
Duplicate:
Relates:

Sub Tasks

Description
JInternalFrame:
If title text is longer than the frame's width, title text's last part should be ellipsis. However title text is drawn until the end of frame width and control buttons like close, minimize button are overlayed on top of frame title text only in Metal and Window looks. 

1) Run SwingSet in WindowNT.
2) Select 'Internal Frame' tab.
3) Make title very long for example 30 characters.
4) Create one.
5) Title text is drawn under control buttons.
bae-chul.kim@eng 1998-04-30

Name: krT82822			Date: 03/15/2000


java version "1.3.0rc1"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.0rc1-T)
Java HotSpot(TM) Client VM (build 1.3.0rc1-S, mixed mode)

The title text shown on a JInternalFrame needs to be clipped, or more correctly
be adjusted to use the "..." suffix like JLabel does, when there isn't enough
room for all the text. This is how native Windows MDI sessions behave. This
results in the title text showing through behind the frame control buttons(ie
minimize, maximize and close) which appears very unprofessional. The problem can
be seen in both the Metal and Windows LFs (I didn't check Motif). You'll only
see the problem w/ Metal for frames that aren't iconified, but for Windows
you'll see it all the time. By adjusting the width of the frame you can see
varying amounts of the title text behind the control buttons.

Use the following test app to see the problem:
import java.awt.event.*;
import java.awt.*;
import javax.swing.*;

public class JDesktopPanePaintBug extends JFrame {
   public static void main(String[] args) {
      try {
         
UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
      } catch (Exception x) {}
      new JDesktopPanePaintBug();
   }

   public JDesktopPanePaintBug() {
      addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent e) {
            System.exit(0);
         }}
      );

      JDesktopPane desktop = new JDesktopPane();
      desktop.putClientProperty("JDesktopPane.dragMode", "outline");
      desktop.setBorder(BorderFactory.createLineBorder(Color.red, 3));
      setContentPane(desktop);

      JInternalFrame iframe = new JInternalFrame("This is a test frame long title", true, false, true, true);
      iframe.setOpaque(true);
      iframe.setBackground(Color.white);
      iframe.setBounds(10,10,200,200);
      iframe.setVisible(true);
      desktop.add(iframe);

      iframe = new JInternalFrame("Short title", true, false, true, true);
      iframe.setOpaque(true);
      iframe.setBackground(Color.white);
      iframe.setBounds(30,30,200,200);
      iframe.setVisible(true);
      desktop.add(iframe);

      pack();
      setBounds(300, 300, 500, 300);
      setVisible(true);
   }
}
(Review ID: 102442)
======================================================================

                                    

Comments
EVALUATION


this is the case indeed. need to verify with Metal L&F proper behaviour, since motif just clips, Windows uses ellipsis but what does Metal want to do?
richard.schiavi@Eng 1998-05-27

btw, yes windows L&F is also incorrect. . .

richard.schiavi@Eng 1998-05-27

Metal should behave like Windows, not like Motif in this case.

hania.gajewska@Eng 2000-02-09

We should get a clipped string for internal frame title before its
title paints if its width less than text length before we paint its
title. (BasicInternalFrameUI.java)

Also in real Windows applications the width of the window can never
be smoller than the width of the first two letters of the title plus
"...". So in BasicInternalFrameUI.installUI the minimum width of the
frame should be calculated. When we add/remove buttons for closing,
iconifying and maximizing the frame this minimum width should be
recalculated. It applicable for Metal and Windows LAF.

For correct recalculating of internal frame's minimum width the
property "iconable" for internal Frame should be bound property.

###@###.### 2000-03-16
                                     
2000-03-16
SUGGESTED FIX


------- JInternalFrame.java -------
*** /tmp/dfoivH_	Thu Mar 16 16:00:20 2000
--- JInternalFrame.java	Thu Mar 16 10:38:39 2000
***************
*** 767,773 ****
--- 767,776 ----
       *     description: Determines whether this frame can be iconified.
       */
      public void setIconifiable(boolean b) {
+         Boolean oldValue = iconable ? Boolean.TRUE : Boolean.FALSE; 
+         Boolean newValue = b ? Boolean.TRUE : Boolean.FALSE;
          iconable = b;
+         firePropertyChange("iconable", oldValue, newValue);
      }
   
      /** 

------- BasicInternalFrameUI.java -------
*** /tmp/d0ROMMO	Thu Mar 16 15:59:20 2000
--- BasicInternalFrameUI.java	Thu Mar 16 15:51:25 2000
***************
*** 97,103 ****
          int height = (getNorthPane() != null ?
  	  getNorthPane().getMinimumSize().height : 0) +
            frame.getInsets().top + frame.getInsets().bottom;
!        frame.setMinimumSize(new Dimension(120, height));
      }   
  
  
--- 97,109 ----
          int height = (getNorthPane() != null ?
  	  getNorthPane().getMinimumSize().height : 0) +
            frame.getInsets().top + frame.getInsets().bottom;
! 	int width = 120;
! 	if (getNorthPane()!=null && getNorthPane() instanceof BasicInternalFrameTitlePane) {
!             width = ((BasicInternalFrameTitlePane)getNorthPane()).getMinimumWidth();
!             if (width == 0)  width = 120;
!             else  width += frame.getInsets().left + frame.getInsets().right;
!         }
! 	frame.setMinimumSize(new Dimension(width, height));
      }   
  
  
***************
*** 451,456 ****
--- 457,472 ----
  		//	glassPane.addMouseMotionListener(glassPaneDispatcher);
  		glassPane.setVisible(true);
  	    }
+ 	} else if (JInternalFrame.TITLE_PROPERTY.equals(prop) || prop.equals("closable") || prop.equals("iconable") || prop.equals("maximizable")) {
+ 	    Dimension dim = frame.getMinimumSize();
+ 	    if (getNorthPane()!=null && getNorthPane() instanceof BasicInternalFrameTitlePane) {
+                 dim.width = ((BasicInternalFrameTitlePane)getNorthPane()).getMinimumWidth();
+                 if (dim.width == 0)  dim.width = 120;  
+                 else  dim.width += frame.getInsets().left + frame.getInsets().right;
+             }
+ 	    frame.setMinimumSize(dim);
+             Dimension frame_dim = frame.getSize();
+             if (dim.width > frame_dim.width) frame.setSize(dim.width, frame_dim.height);
  	} else if ( prop.equals("ancestor") ) {
  	  if ((frame.getParent() != null) && !componentListenerAdded ) {
  	    f.getParent().addComponentListener(componentListener);

------- BasicInternalFrameTitlePane.java -------
*** /tmp/d__Clc_	Thu Mar 16 15:59:40 2000
--- BasicInternalFrameTitlePane.java	Thu Mar 16 10:49:57 2000
***************
*** 1,5 ****
  /*
!  * %W% %E%
   *
   * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
   * 
--- 1,5 ----
  /*
!  * @(#)BasicInternalFrameTitlePane.java	1.32 00/02/21
   *
   * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
   * 
***************
*** 32,38 ****
   * version of Swing.  A future release of Swing will provide support for
   * long term persistence.
   *
!  * @version %I% %G%
   * @author David Kloba
   * @author Steve Wilson
   */
--- 32,38 ----
   * version of Swing.  A future release of Swing will provide support for
   * long term persistence.
   *
!  * @version 1.32 02/21/00
   * @author David Kloba
   * @author Steve Wilson
   */
***************
*** 262,280 ****
                  fm.getAscent() + fm.getLeading();
  
              int titleX;
!             String title = frame.getTitle();
              if( BasicGraphicsUtils.isLeftToRight(frame) ) {
                  titleX = menuBar.getX() + menuBar.getWidth() + 2;
              } else {
                  titleX = menuBar.getX() - 2
                           - SwingUtilities.computeStringWidth(fm,title);
!             }
!             
  	    g.drawString(title, titleX, baseline);
  	    g.setFont(f);
  	}
      }
  
      /**
       * Post a WINDOW_CLOSING-like event to the frame, so that it can
       * be treated like a regular Frame.
--- 262,325 ----
                  fm.getAscent() + fm.getLeading();
  
              int titleX;
! 	    Rectangle r = new Rectangle(0, 0, 0, 0);
! 	    if (frame.isIconifiable())  r = iconButton.getBounds();
! 	    else if (frame.isMaximizable())  r = maxButton.getBounds();
! 	    else if (frame.isClosable())  r = closeButton.getBounds();
! 	    int titleW;
! 	
!             String title;
              if( BasicGraphicsUtils.isLeftToRight(frame) ) {
+ 		if (r.x == 0)  r.x = frame.getWidth()-frame.getInsets().right;
                  titleX = menuBar.getX() + menuBar.getWidth() + 2;
+ 		titleW = r.x - titleX - 3;
+ 		title = clippedText(frame.getTitle(), fm, titleW);
              } else {
+ 		titleW = menuBar.getX() - r.x -r.width - 4;
+ 		title = clippedText(frame.getTitle(), fm, titleW);
+ 
                  titleX = menuBar.getX() - 2
                           - SwingUtilities.computeStringWidth(fm,title);
!            }
! 
  	    g.drawString(title, titleX, baseline);
  	    g.setFont(f);
  	}
      }
  
+     protected String clippedText(String  text, FontMetrics fm, int availTextWidth) {
+ 	if ( (text == null) || (text.equals("")) )  return "";
+ 	int textWidth = SwingUtilities.computeStringWidth(fm, text);
+ 	String clipString = "...";
+ 	if (textWidth > availTextWidth) {
+ 	    int totalWidth = SwingUtilities.computeStringWidth(fm, clipString);
+ 	    int nChars;
+ 	    for(nChars = 0; nChars < text.length(); nChars++) {
+ 		totalWidth += fm.charWidth(text.charAt(nChars));
+ 		if (totalWidth > availTextWidth) {
+ 		    break;
+ 		}
+ 	    }
+ 	    text = text.substring(0, nChars) + clipString;
+ 	}
+ 	return text;
+     }
+ 
+     public int getMinimumWidth() {
+ 	int w = 22;
+ 	if(frame.isClosable())  w += 19;
+ 	if(frame.isMaximizable())  w += 19;        
+ 	if(frame.isIconifiable())  w += 19;
+ 	FontMetrics fm = getFontMetrics(UIManager.getFont("InternalFrame.titleFont"));
+ 	int title_w = fm.stringWidth(frame.getTitle());
+ 	if (frame.getTitle().length()>2) {
+ 	    int subtitle_w = fm.stringWidth(frame.getTitle().substring(0, 2) + "...");
+ 	    w += (title_w < subtitle_w) ? title_w : subtitle_w;
+ 	}
+ 	else w = title_w;
+ 	return w;
+     }
+ 
      /**
       * Post a WINDOW_CLOSING-like event to the frame, so that it can
       * be treated like a regular Frame.
***************
*** 346,352 ****
                      add(maxButton);
                  else
                      remove(maxButton);
!             } else if( prop.equals("iconifiable") ) {
                  if( (Boolean)evt.getNewValue() == Boolean.TRUE )
                      add(iconButton);
                  else
--- 391,397 ----
                      add(maxButton);
                  else
                      remove(maxButton);
!             } else if( prop.equals("iconable") ) {
                  if( (Boolean)evt.getNewValue() == Boolean.TRUE )
                      add(iconButton);
                  else

------- MetalInternalFrameTitlePane.java -------
*** /tmp/dUMa4.s	Thu Mar  2 20:11:14 2000
--- MetalInternalFrameTitlePane.java	Thu Mar  2 19:00:55 2000
***************
*** 311,323 ****
              g.setFont(f);
              FontMetrics fm = g.getFontMetrics();
              int fHeight = fm.getHeight();
-             titleLength = fm.stringWidth(frameTitle);
  
              g.setColor(foreground);
  
              int yOffset = ( (height - fm.getHeight() ) / 2 ) + fm.getAscent();
!             if( !leftToRight )
!                 xOffset -= titleLength;
              g.drawString( frameTitle, xOffset, yOffset );
              xOffset += leftToRight ? titleLength + 5  : -5;
          }
--- 311,338 ----
              g.setFont(f);
              FontMetrics fm = g.getFontMetrics();
              int fHeight = fm.getHeight();
  
              g.setColor(foreground);
  
              int yOffset = ( (height - fm.getHeight() ) / 2 ) + fm.getAscent();
! 
! 	    Rectangle rect = new Rectangle(0, 0, 0, 0);
! 	    if (frame.isIconifiable())  rect = iconButton.getBounds();
! 	    else if (frame.isMaximizable())  rect = maxButton.getBounds();
! 	    else if (frame.isClosable())  rect = closeButton.getBounds();
! 	    int titleW;
! 	    
!             if( leftToRight ) {
! 		if (rect.x == 0)  rect.x = frame.getWidth()-frame.getInsets().right-2;
! 		titleW = rect.x - xOffset - 4;
! 		frameTitle = clippedText(frameTitle, fm, titleW);
!             } else {
! 		titleW = xOffset - rect.x - rect.width - 4;
! 		frameTitle = clippedText(frameTitle, fm, titleW);
! 		xOffset -= SwingUtilities.computeStringWidth(fm, frameTitle);
! 	    }
!             
! 	    titleLength = SwingUtilities.computeStringWidth(fm, frameTitle);
              g.drawString( frameTitle, xOffset, yOffset );
              xOffset += leftToRight ? titleLength + 5  : -5;
          }
***************
*** 325,331 ****
          int bumpXOffset;
          int bumpLength;
          if( leftToRight ) {
!             bumpLength = width - buttonsWidth - xOffset -5;
              bumpXOffset = xOffset;
          } else {
              bumpLength = xOffset - buttonsWidth - 5;
--- 340,346 ----
          int bumpXOffset;
          int bumpLength;
          if( leftToRight ) {
!             bumpLength = width - buttonsWidth - xOffset - 5;
              bumpXOffset = xOffset;
          } else {
              bumpLength = xOffset - buttonsWidth - 5;
***************
*** 336,342 ****
          bumps.setBumpArea( bumpLength, bumpHeight );
          bumps.paintIcon(this, g, bumpXOffset, bumpYOffset);
      }
! 					     				    
      public void setPalette(boolean b) {
          isPalette = b;
  
--- 351,373 ----
          bumps.setBumpArea( bumpLength, bumpHeight );
          bumps.paintIcon(this, g, bumpXOffset, bumpYOffset);
      }
!    					
!     public int getMinimumWidth() {
! 	int w = 30;
! 	if(frame.isClosable())  w += 21;
! 	if(frame.isMaximizable())  w += 16 + (frame.isClosable() ? 10 : 4);
! 	if(frame.isIconifiable())  w += 16 + (frame.isMaximizable() ? 2 : (frame.isClosable() ? 10 : 4));
! 
! 	FontMetrics fm = getFontMetrics(UIManager.getFont("InternalFrame.titleFont"));
! 	int title_w = fm.stringWidth(frame.getTitle());
! 	if (frame.getTitle().length()>2) {
! 	    int subtitle_w = fm.stringWidth(frame.getTitle().substring(0, 2) + "...");
! 	    w += (title_w < subtitle_w) ? title_w : subtitle_w;
! 	}
! 	else w = title_w;
! 	return w;
!     }    
!    		    
      public void setPalette(boolean b) {
          isPalette = b;

###@###.### 2000-03-16
                                     
2000-03-16
CONVERTED DATA

BugTraq+ Release Management Values

COMMIT TO FIX:
merlin-beta

FIXED IN:
merlin-beta

INTEGRATED IN:
merlin-beta

VERIFIED IN:
merlin-beta


                                     
2004-06-14



Hardware and Software, Engineered to Work Together