Want to protect your cyber security and still get fast solutions? Ask a secure question today.Go Premium

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 429
  • Last Modified:

StylePad demo - can you make it work with a scaled Graphics2D instance?


Is anyone here able to get their /jfc/Stylepad/ demo located in the J2SDK directory to work 100% with a scaled Graphics2D instance?

When I serve it a scaled Graphicds2D instance I get these problems;
- Selection does not reflect mouse position
- The cursor is not positioned right (writing a lot of text it looks like it's in the middle but it should be at the end). In addition to this, it is not blinking.
- Stuff get "messed up" all around the component

Everything should work like normal with a scale from 0.2 to 2 ..
0
borbjo
Asked:
borbjo
  • 9
  • 8
1 Solution
 
OviCommented:
To which components you serve this scalled instance?
0
 
borbjoAuthor Commented:
To the JTextComponent's paintComponent() method

here's my replacement for the createEditor() method in the StylePad demo (various test code around, but you get the point):

    protected JTextComponent createEditor() {
           StyleContext sc = new StyleContext();
           final DefaultStyledDocument doc = new DefaultStyledDocument();
              initDocument(doc, sc);
          final JTextPane p = new JTextPane(doc){
                 public void paintComponent(Graphics g) {
                        Graphics2D g2d = (Graphics2D)g;
                     g2d.scale(2,2);
                     super.paintComponent(g2d);


        Highlighter hl = this.getHighlighter();
        //hl.removeAllHighlights();
        int start = doc.getCaretPos();
        int end = doc.getSecondCaretPos();

        if( start != end && end != -1 && start != -1) {    // secondCaretPos -1 means no text selected
            if(start > end) {
                int tmp = start;
                start = end;
                end = tmp;
            }
            try {
                hl.addHighlight(start, end, new DefaultHighlighter.DefaultHighlightPainter( new Color(204, 204, 255) ) );
                hl.paint(g2d);
            } catch (BadLocationException e) {
                e.printStackTrace();  // just print the stack trace ..
            }
        }


            try {
                int pos = doc.getCaretPos();

                System.out.println("caretPos="+pos);

                Rectangle r = this.modelToView(pos);

                AttributeSet b = doc.getParagraphElement(pos).getAttributes();
                AttributeSet a = doc.getCharacterElement(pos).getAttributes();

                Font f = doc.getFont( a );
                if(f == null) f = doc.getFont( b );

                //logger.debug("width="+width+"+"+"width("+doc.getText(x,1)+")");
                //int width = tp.getFontMetrics( f ).stringWidth( doc.getText(pos,1) );


               FontMetrics fm = this.getFontMetrics(f);
               int width = fm.charWidth((doc.getText(pos,1)).charAt(0));
               int height = fm.getHeight();
               g2d.setColor(Color.red);
               g2d.fillRect(r.x, r.y, 1, height);

                /*
                int posStart = tp.viewToModel(new Point(0,r.y));
                int posEnd = pos;

                Rectangle startr = tp.modelToView(posStart);

                int width = 0;
                for(int x = posStart; x < posEnd; x++) {
                    AttributeSet b = doc.getParagraphElement(x).getAttributes();
                    AttributeSet a = doc.getCharacterElement(x).getAttributes();

                    Font f = doc.getFont( a );
                    if(f == null) f = doc.getFont( b );

                    logger.debug("width="+width+"+"+"width("+doc.getText(x,1)+")");
                    width += tp.getFontMetrics( f ).stringWidth( doc.getText(x,1) );
                }
                logger.debug("posStart="+posStart+", posEnd="+posEnd+", width="+width+", char="+doc.getText(posEnd-1,1));
                //logger.debug("f="+f.getFamily()+" - "+doc.getText(pos,1) + " - width="+width + ", posx="+posx+", r.x="+r.x);
                */
                //g2d.setColor(caretColor);
                //g2d.fillRect(posx+r.x+width, posy+r.y, 1, r.height);
            } catch (Exception e) {
                e.printStackTrace();  // just print the stack trace ..
            }
        }


                    /* Caret c = getCaret();
                           if (c!= null) {
                                 try {                                  
                                                int pos = c.getDot();
                                           Rectangle r = modelToView(pos);
                                         DefaultStyledDocument doc = (DefaultStyledDocument)getDocument();
                                               AttributeSet b = doc.getParagraphElement(pos).getAttributes();
                                          AttributeSet a = doc.getCharacterElement(pos).getAttributes();
                                          Font f = doc.getFont( a );
                                              if(f == null) f = doc.getFont( b );
                                             int width = getFontMetrics( f ).stringWidth( doc.getText(pos,1) );
                                              g.setColor(Color.BLUE);
                                         g.fillRect(r.x+width, r.y, 1, r.height);
                                       }
                                       catch (Exception ex) {
                                          System.out.println(ex);
                                 }
                               }*/

               };
              p.addCaretListener(new CaretListener() {
                        public void caretUpdate(CaretEvent e) {
                         p.repaint();
                    }
               });
             MouseInputAdapter mh = new MouseInputAdapter() {
                        public void mousePressed(MouseEvent e) {
                                p.repaint();
                   }
                       public void mouseDragged(MouseEvent e) {
                                p.repaint();
                   }
               };
              p.addMouseListener(mh);
         p.addMouseMotionListener(mh);
           p.setDragEnabled(true);
       
        //p.getCaret().setBlinkRate(0);
       
        return p;
    }
0
 
OviCommented:
I didn't had the time to test your code, probably I'll do'it today but few considerations:
1. Normally all swing components should be able and as far as I know they allready use Graphics2D instance.
2. Since you pass a scalled graphics instance this should be reflected in the manipulation of the document model too. The text document model does not know the way in which the text is represented. The erectangle obtained by you with Rectangle r = this.modelToView(pos), is not a scalled rectangle so you should apply scale factors to it too.
0
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 
borbjoAuthor Commented:

ah yes .. that is a valid point.

But still, it does not fix the fact that the original cursor of the JTextComponent is misplaced as well ..

I use the rectangle to draw a line where I think the cursor should be ...
0
 
OviCommented:
Note that by changing the default graphics to a scalled instance will affect the all mvc_view of the document. The scalling should be then propagated in all code, and perhaps you must write your own views xinstances for various element types to reflect that change. I will take a look, and hope there must be done only some simple modifications.
0
 
borbjoAuthor Commented:
Have you had time to look at it yet?

I didn't quite understand what you meant by "all mvc_view" of the document .. isn't all painting filtered through the JTextComponent's paintComponent() method? If so, it is scaled there?

Best regards,
Bjorn
0
 
borbjoAuthor Commented:
Here's some info about the View;

"A View has the following semantics for rendering:
* The view gets it's allocation (Graphics context) from the parent at paint time

* The coordinate system is the same as the hosting Component.

* This means a child view lives in the same coordinate system as the parent view.

* Rendering is performed by traversing down the tree of View implementations.

* Each View is responsible for rendering its children.

* The order of views relative to the model is up to the implementation.

* Although child views will typically be arranged in the same order that they occur in the model, they may be visually arranged in an entirely different order. "
http://www.cs.du.edu/~sdeshpan/comp4708/Lesson1/Part5.htm

So, changing the top view (paintComponent() in JTextComponent) should be enough .. I would think..

- bjorn
0
 
borbjoAuthor Commented:
Creating a new ScaledCaret helped a bit (this ScaledCaret is just a test and it works when g2d.scale(0.5,0.5) is set in the JTextPane's paintComponent()):

---------------------------------------------------

import javax.swing.text.DefaultCaret;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;

public class ScaledCaret extends DefaultCaret {

    /**
     * Moves the caret position
     * according to the mouse pointer's current
     * location.  This effectively extends the
     * selection.  By default, this is only done
     * for mouse button 1.
     *
     * @param e the mouse event
     * @see MouseMotionListener#mouseDragged
     */
    public void mouseDragged(MouseEvent e) {
        MouseEvent p = new MouseEvent((Component)e.getSource(),e.getID(),e.getWhen(),e.getModifiers(),(int)Math.round(e.getX()*2), (int)Math.round(e.getY()*2),e.getClickCount(),e.isPopupTrigger());
        super.mouseDragged(p);
    }

    /**
     * Tries to set the position of the caret from
     * the coordinates of a mouse event, using viewToModel().
     *
     * @param e the mouse event
     */
    protected void positionCaret(MouseEvent e) {
        MouseEvent p = new MouseEvent((Component)e.getSource(),e.getID(),e.getWhen(),e.getModifiers(),(int)Math.round(e.getX()*2), (int)Math.round(e.getY()*2),e.getClickCount(),e.isPopupTrigger());
        super.positionCaret(p);
    }
}
--------------------------------------------------------



Now the caret location seems to be ok, however, the components around / scrolling etc. is still messed up .. I've tried setting the size of the JTextComponent after the scale, but it didn't work ..
0
 
OviCommented:
The paintComponent(...) method will not do the hole thing.

Read followings:
http://java.sun.com/products/jfc/tsc/articles/text/concurrency/index.html

and all articles regarding swing text package from:

http://java.sun.com/products/jfc/tsc/articles/
0
 
OviCommented:
Your code in method createEditor() cannot be compiled.
Example:

        int start = doc.getCaretPos();
       int end = doc.getSecondCaretPos();

those two methods does not exists in the implementation of a DefaultStyledDocument. You are using a special document of yours?
0
 
borbjoAuthor Commented:
ah, yes I am .. sorry, just use this one instead:


    protected JTextComponent createEditor() {
           StyleContext sc = new StyleContext();
           final DefaultStyledDocument doc = new DefaultStyledDocument();
              initDocument(doc, sc);
          final JTextPane p = new JTextPane(doc){



              public int viewToModel(Point p) {
                  System.out.println("viewToModel("+p+") was called");
                  int pos = super.viewToModel(p);
                  return pos;
              }

              public void paintComponent(Graphics g) {
                        Graphics2D g2d = (Graphics2D)g;
                     g2d.scale(0.5,0.5);
                     //this.setSize( (int)Math.round(this.getBounds().width*0.5),(int)Math.round(this.getBounds().height*0.5) );
                     super.paintComponent(g2d);


                }



               };
                p.setCaret(new ScaledCaret());
                p.addCaretListener(new CaretListener() {
                        public void caretUpdate(CaretEvent e) {

                            p.repaint();
                    }
               });
             MouseInputAdapter mh = new MouseInputAdapter() {
                        public void mousePressed(MouseEvent e) {
                                p.repaint();
                   }
                       public void mouseDragged(MouseEvent e) {
                                p.repaint();
                   }
               };
                p.addMouseListener(mh);
                p.addMouseMotionListener(mh);
                p.setDragEnabled(true);


        return p;
    }
0
 
OviCommented:
you should overwrite the default TextUI instance. If you are looking (tracking) the calls from your scalled caret, you will see that in the DefaultCaret implementation there are some calls to viewToModel() and possible to modelToView() methods.

    /**
     * Tries to move the position of the caret from
     * the coordinates of a mouse event, using viewToModel().
     * This will cause a selection if the dot and mark
     * are different.
     *
     * @param e the mouse event
     */
    protected void moveCaret(MouseEvent e) {
     Point pt = new Point(e.getX(), e.getY());
     Position.Bias[] biasRet = new Position.Bias[1];
     int pos = component.getUI().viewToModel(component, pt, biasRet);
     if(biasRet[0] == null)
         biasRet[0] = Position.Bias.Forward;
     if (pos >= 0) {
         moveDot(pos, biasRet[0]);
     }
    }
0
 
borbjoAuthor Commented:
Yes, I think I need to create my own BasicTextPaneUI implementation.

But the caret behaviour is more or less okay now, except from it not blinking. What's bugging me now is the width of the scaled JTextComponent.

The background is white and has the correct size, here's the method that paints it copied from BasicTextUI:

    /**
     * Paints a background for the view.  This will only be
     * called if isOpaque() on the associated component is
     * true.  The default is to paint the background color
     * of the component.
     *
     * @param g the graphics context
     */
    protected void paintBackground(Graphics g) {
        g.setColor(editor.getBackground());
        g.fillRect(0, 0, editor.getWidth(), editor.getHeight());
    }


Since the white background is painted in the correct size, editor.getWidth() etc. must be correct ..

however, the component itself displays a gray area to the right of it where lots of garbage is painted ...

0
 
OviCommented:
that's because the proper width cannot be calculated exactly, because of the ui itself. If you are looking up to the parent of your text component, you'll see that the getPrefferedSize is overwriten in JEditorPane class:

    public Dimension getPreferredSize() {
      Dimension d = super.getPreferredSize();
      if (getParent() instanceof JViewport) {
          JViewport port = (JViewport)getParent();
          TextUI ui = getUI();
            int prefWidth = d.width;
            int prefHeight = d.height;
          if (! getScrollableTracksViewportWidth()) {
            int w = port.getWidth();
            Dimension min = ui.getMinimumSize(this);
            if (w != 0 && w < min.width) {
                    // Only adjust to min if we have a valid size
                prefWidth = min.width;
            }
          }
          if (! getScrollableTracksViewportHeight()) {
            int h = port.getHeight();
            Dimension min = ui.getMinimumSize(this);
            if (h != 0 && h < min.height) {
                    // Only adjust to min if we have a valid size
                prefHeight = min.height;
            }
          }
            if (prefWidth != d.width || prefHeight != d.height) {
                d = new Dimension(prefWidth, prefHeight);
            }
      }
      return d;
    }

By the way, the caret does not work if you change the scalling factors. The posted code works for 0.5 scale. If you try another value, is not working anymore.

Use this class to have a general point of accessing the same scale:

/*
 * Created on Mar 14, 2003
 *
 * To change this generated comment go to
 * Window>Preferences>Java>Code Generation>Code Template
 */

/**
 * @author ovi
 */
public class StylePadConstants {
  public static final double X_SCALE_FACTOR = 0.5;
      public static final double Y_SCALE_FACTOR = 0.5;
}


.......................................

      protected JTextComponent createEditor() {
            StyleContext sc = new StyleContext();
            final DefaultStyledDocument doc = new DefaultStyledDocument();
            initDocument(doc, sc);
            final JTextPane p = new JTextPane(doc) {
                  public int viewToModel(Point p) {
                        System.out.println("viewToModel(" + p + ") was called");
                        //                        Point scaledPoint = new Point((int) Math.round(p.x * StylePadConstants.X_SCALE_FACTOR), (int) Math.round(p.y * StylePadConstants.Y_SCALE_FACTOR));
                        int pos = super.viewToModel(p);
                        return pos;
                  }

                  public Dimension getPreferredSize() {
                        Dimension d = super.getPreferredSize();
                        if (getParent() instanceof JViewport) {
                              JViewport port = (JViewport) getParent();
                              TextUI ui = getUI();
                              int prefWidth = d.width;
                              int prefHeight = d.height;
                              if (!getScrollableTracksViewportWidth()) {
                                    int w = port.getWidth();
                                    Dimension min = ui.getMinimumSize(this);
                                    if (w != 0 && w < min.width) {
                                          // Only adjust to min if we have a valid size
                                          prefWidth = min.width;
                                    }
                              }
                              if (!getScrollableTracksViewportHeight()) {
                                    int h = port.getHeight();
                                    Dimension min = ui.getMinimumSize(this);
                                    if (h != 0 && h < min.height) {
                                          // Only adjust to min if we have a valid size
                                          prefHeight = min.height;
                                    }
                              }
                              if (prefWidth != d.width || prefHeight != d.height) {
                                    d = new Dimension(prefWidth, prefHeight);
                              }
                        }
                        double w = d.getWidth();
                        double h = d.getHeight();
                        d = new Dimension((int) Math.round(w * StylePadConstants.X_SCALE_FACTOR), (int) Math.round(h * StylePadConstants.Y_SCALE_FACTOR));
                        return d;
                  }
                  
                  public void paintComponent(Graphics g) {
                        Graphics2D g2d = (Graphics2D) g;
                        g2d.scale(StylePadConstants.X_SCALE_FACTOR, StylePadConstants.Y_SCALE_FACTOR);
                        //this.setSize( (int)Math.round(this.getBounds().width*0.5),(int)Math.round(this.getBounds().height*0.5) );
                        super.paintComponent(g2d);
                  }
            };
            p.setCaret(new ScaledCaret());
            p.addCaretListener(new CaretListener() {
                  public void caretUpdate(CaretEvent e) {
                        p.repaint();
                  }
            });
            MouseInputAdapter mh = new MouseInputAdapter() {
                  public void mousePressed(MouseEvent e) {
                        p.repaint();
                  }
                  public void mouseDragged(MouseEvent e) {
                        p.repaint();
                  }
            };
            p.addMouseListener(mh);
            p.addMouseMotionListener(mh);
            p.setDragEnabled(true);
            return p;
      }

..........................................

import java.awt.Component;
import java.awt.event.MouseEvent;

import javax.swing.text.DefaultCaret;

public class ScaledCaret extends DefaultCaret {

      /**
       * Moves the caret position
       * according to the mouse pointer's current
       * location.  This effectively extends the
       * selection.  By default, this is only done
       * for mouse button 1.
       *
       * @param e the mouse event
       * @see MouseMotionListener#mouseDragged
       */
      public void mouseDragged(MouseEvent e) {
            MouseEvent p = new MouseEvent((Component) e.getSource(), e.getID(), e.getWhen(), e.getModifiers(), (int) Math.round(e.getX() * /*2),//*/
            StylePadConstants.X_SCALE_FACTOR), (int) Math.round(e.getY() * /*2),//*/
            StylePadConstants.Y_SCALE_FACTOR), e.getClickCount(), e.isPopupTrigger());
            super.mouseDragged(p);
            //      super.mouseDragged(e);
      }

      /**
       * Tries to set the position of the caret from
       * the coordinates of a mouse event, using viewToModel().
       *
       * @param e the mouse event
       */
      protected void positionCaret(MouseEvent e) {
            MouseEvent p = new MouseEvent((Component) e.getSource(), e.getID(), e.getWhen(), e.getModifiers(), (int) Math.round(e.getX() * /*2),//*/
            StylePadConstants.X_SCALE_FACTOR), (int) Math.round(e.getY() * /*2),//*/
            StylePadConstants.Y_SCALE_FACTOR), e.getClickCount(), e.isPopupTrigger());
            super.positionCaret(p);
            //      super.positionCaret(e);
      }
}
0
 
borbjoAuthor Commented:
You use IDEA as well I see ;) .. good choice! :)

Overriding getPreferredSize() in the JTextComponent did not seem to have an effect on the problems with the garbage printing etc .. really strange - it seems that the JScrollPane thinks the component (the JTextComponent) is twice as big as what it is .. when I resize it the JTextComponent always only fills half the space ..

>By the way, the caret does not work if you change the
> scalling factors. The posted code works for 0.5 scale.
> If you try another value, is not working anymore.

I know, I was just using it as a test code .. You'd made a slight error in your code btw, you have to divde not multiply in the caret class .. here's the fixed version;

import java.awt.Component;
import java.awt.event.MouseEvent;

import javax.swing.text.DefaultCaret;

public class ScaledCaret extends DefaultCaret {

     /**
      * Moves the caret position
      * according to the mouse pointer's current
      * location.  This effectively extends the
      * selection.  By default, this is only done
      * for mouse button 1.
      *
      * @param e the mouse event
      * @see java.awt.event.MouseMotionListener#mouseDragged
      */
     public void mouseDragged(MouseEvent e) {
          MouseEvent p = new MouseEvent((Component) e.getSource(), e.getID(), e.getWhen(), e.getModifiers(), (int) Math.round(e.getX() /
          StylePadConstants.X_SCALE_FACTOR), (int) Math.round(e.getY() /
          StylePadConstants.Y_SCALE_FACTOR), e.getClickCount(), e.isPopupTrigger());
          super.mouseDragged(p);
     }

     /**
      * Tries to set the position of the caret from
      * the coordinates of a mouse event, using viewToModel().
      *
      * @param e the mouse event
      */
     protected void positionCaret(MouseEvent e) {
          MouseEvent p = new MouseEvent((Component) e.getSource(), e.getID(), e.getWhen(), e.getModifiers(), (int) Math.round(e.getX() /
          StylePadConstants.X_SCALE_FACTOR), (int) Math.round(e.getY() /
          StylePadConstants.Y_SCALE_FACTOR), e.getClickCount(), e.isPopupTrigger());
          super.positionCaret(p);
     }

    /**
     * Tries to move the position of the caret from
     * the coordinates of a mouse event, using viewToModel().
     * This will cause a selection if the dot and mark
     * are different.
     *
     * @param e the mouse event
     */
    protected void moveCaret(MouseEvent e) {
          MouseEvent p = new MouseEvent((Component) e.getSource(), e.getID(), e.getWhen(), e.getModifiers(), (int) Math.round(e.getX() /
          StylePadConstants.X_SCALE_FACTOR), (int) Math.round(e.getY() /
          StylePadConstants.Y_SCALE_FACTOR), e.getClickCount(), e.isPopupTrigger());
        super.moveCaret(p);
    }
}
0
 
borbjoAuthor Commented:
I didn't quite make it, but nevertheless your comments helped me along the way.
0
 
OviCommented:
Thanks.
0

Featured Post

Get expert help—faster!

Need expert help—fast? Use the Help Bell for personalized assistance getting answers to your important questions.

  • 9
  • 8
Tackle projects and never again get stuck behind a technical roadblock.
Join Now