Some days ago I was trying to find a solution for a multiline label widget for the netbeans visual library, I tried some solutions (out of the box) like QLabel and others found on the web, without any success, after 2 weeks of frustrating research, I decided to continue with other features of my system, and then later on, I returned to this issue again and give it a try.
The problem:
I needed a Widget that supported Multiline label, auto-fit on resize, and drag and drop action, the first approach was to include QLabel within a ComponentWidget, It did not work for me,  and then, I tried with a JTextArea in this way:
public class MultilineLabelWidget
    extends ComponentWidget
{
    public MultilineLabelWidget( Scene scene, String text )
    {
        super( scene, new JTextArea( text ) );
        JTextArea jText = (JTextArea) this.getComponent();
        jText.setOpaque( false );
        jText.setEditable( false );
        jText.setLineWrap( true );
        jText.setWrapStyleWord( true );
        jText.setHighlighter( null );
        jText.setBorder( BorderFactory.createEmptyBorder() );
        
        this.getActions().addAction( ActionFactory.createResizeAction() );
        this.getActions().addAction( ActionFactory.createMoveAction() );
        this.setBorder( org.netbeans.api.visual.border.BorderFactory.createLineBorder( 8 ) );
    }
}
and It partially worked!!, the problem is that JTextArea is painted after the widget, so the MoveAction will not work! out-of-the-box, ummm, I tried another solution, not extending the ComponentWidget but creating an in-line ComponentWidget and trying to catch the Mouse Events and redirect the calls to its Action in the Widget, like this:
JTextArea jText = new JTextArea( hm );
        jText.setOpaque( false );
        jText.setEditable( false );
        jText.setLineWrap( true );
        jText.setWrapStyleWord( true );
        jText.setBorder( BorderFactory.createEmptyBorder() );
        final ComponentWidget widget = new ComponentWidget(scene,jText);
        widget.getActions().addAction( ActionFactory.createResizeAction() );
        widget.getActions().addAction( ActionFactory.createMoveAction() );
        jText.addMouseMotionListener( new MouseMotionListener(){
            @Override
            public void mouseDragged( MouseEvent event )
            {
                widget.getActions().mouseDragged( widget, new WidgetAction.WidgetMouseEvent(new Date().getTime(),event) );                
            }
            @Override
            public void mouseMoved( MouseEvent event )
            {
                widget.getActions().mouseMoved( widget, new WidgetAction.WidgetMouseEvent(new Date().getTime(),event) );                
            }} );
        
        jText.addMouseListener( new MouseListener(){
            @Override
            public void mouseClicked( MouseEvent event )
            {
                widget.getActions().mouseClicked( widget, new WidgetAction.WidgetMouseEvent(new Date().getTime(),event) );
            }
            @Override
            public void mouseEntered( MouseEvent event )
            {
                widget.getActions().mouseEntered( widget, new WidgetAction.WidgetMouseEvent(new Date().getTime(),event) );
            }
            @Override
            public void mouseExited( MouseEvent event )
            {
                widget.getActions().mouseExited( widget, new WidgetAction.WidgetMouseEvent(new Date().getTime(),event) );
            }
            @Override
            public void mousePressed( MouseEvent event )
            {
                widget.getActions().mousePressed( widget, new WidgetAction.WidgetMouseEvent(new Date().getTime(),event) );
            }
            
            @Override
            public void mouseReleased( MouseEvent event )
            {
                widget.getActions().mouseReleased( widget, new WidgetAction.WidgetMouseEvent(new Date().getTime(),event) );
            }} );
        jText.setHighlighter( null );
        widget.setBorder( org.netbeans.api.visual.border.BorderFactory.createLineBorder( 8 ) );
It worked better than the previous solution!, BUT!!!!!!!!!, It just worked when I moved the mouse pointer slowly (of course, first click and then drag), but every time I moved the mouse pointer as I usually do, the component was not repainted (It did not followed the mouse pointer), and at the moment I released the left button on the mouse, the widget was painted!, frustrating for me because I think It should be easy to do it! (please developer of Visual Library, include a Multiline Label Widget Out-of-the-box).
The solution
Finally, I found a code on the net (It references to com.exalto.UI) and adapted it to fit within the Visual Library Widget, so the result It is this:
package test.designer.widget;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.font.LineBreakMeasurer;
import java.awt.font.TextAttribute;
import java.awt.font.TextLayout;
import java.text.AttributedCharacterIterator;
import java.text.AttributedString;
import org.netbeans.api.visual.widget.LabelWidget;
import org.netbeans.api.visual.widget.Scene;
public class MultilineLabelWidget
    extends LabelWidget
{
     private boolean justify;
    public MultilineLabelWidget( Scene scene, String label )
    {
        super( scene );
        this.setLabel( label );
        this.setJustified( true );
    }
    @Override
    protected void paintWidget()
    {
        paintOrGetSize( this.getGraphics() );
    }
    private void paintOrGetSize( Graphics2D gr )
    {
        float width = (float) ( this.getBounds() != null ? this.getBounds().getWidth() : this.getPreferredSize()
            .getWidth() );
        Insets insets = this.getBorder().getInsets();
        float rwidth = width - ( insets.left + insets.right );// + margin.left + margin.right;
        Rectangle rec = this.calculateClientArea();
        gr.setFont( getFont() );
float x = 0.0F;//+ margin.left;
        float y = (float) rec.getY();//+ margin.top;
        if ( rwidth > 0 && this.getLabel() != null && this.getLabel().length() > 0 )
        {
            AttributedString as = new AttributedString( this.getLabel() );
            as.addAttribute( TextAttribute.FONT, getFont() );
            AttributedCharacterIterator aci = as.getIterator();
            LineBreakMeasurer lbm = new LineBreakMeasurer( aci, gr.getFontRenderContext() );
            
            while ( lbm.getPosition() < aci.getEndIndex() )
            {
                TextLayout textLayout = lbm.nextLayout( rwidth );
                if ( gr != null && isJustified() && textLayout.getVisibleAdvance() > 0.80 * rwidth )
                {
                    textLayout = textLayout.getJustifiedLayout( rwidth );
                }
                if ( gr != null )
                {
                    textLayout.draw( gr, x, y + textLayout.getAscent() );
                }
                y += textLayout.getDescent() + textLayout.getLeading() + textLayout.getAscent();
                
            }
        }
    }
    public boolean isJustified()
    {
        return justify;
    }
    public void setJustified( boolean justify )
    {
        boolean old = this.justify;
        this.justify = justify;
        if ( old != this.justify )
        {
            repaint();
        }
    }
}
It worked for me, It worked for my purposes, I hope It works for you.
Please modify it, improve the code, see if It works for you, and feedback to me.
Thanks a lot.
References:
http://graph.netbeans.org/servlets/ReadMsg?listName=users&msgNo=1295
http://graph.netbeans.org/servlets/ReadMsg?list=users&msgNo=726
http://www.koders.com/java/fid9BE9B31AC9BED01828448BF91A61AFA5AE431E16.aspx
 
 
9 comentarios:
I solved the problem by creating a widget with some LabelWidget inside it... It worked for me.
Your blog keeps getting better and better! Your older articles are not as good as newer ones you have a lot more creativity and originality now keep it up!
Hi Barenca,
I saw your post and seems like i am also in similar situation where i need to have a multiline label widget like MS word flowchart(rectancle / squares .. where you type in some long texts). How ever when i tried to use you solution .. the labels beacme invisible only on mouse hovering it showed up . but still no line break ups (i.e. still in single line). it would be great if you can post or send personally a samll but working example .. it would be really helpful.
Thanks
Rajiv Jha
rajive.jha@gmail.com
Just really love you guys… what fantastic and well researched information. Thank you bunches.
You are so nice to share these with us.
A useful tip
Publicar un comentario