?
Solved

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

Posted on 2003-03-12
17
Medium Priority
?
422 Views
Last Modified: 2010-05-18

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
Comment
Question by:borbjo
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 9
  • 8
17 Comments
 
LVL 9

Expert Comment

by:Ovi
ID: 8118347
To which components you serve this scalled instance?
0
 

Author Comment

by:borbjo
ID: 8118498
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
 
LVL 9

Expert Comment

by:Ovi
ID: 8125803
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.

 

Author Comment

by:borbjo
ID: 8125996

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
 
LVL 9

Expert Comment

by:Ovi
ID: 8126966
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
 

Author Comment

by:borbjo
ID: 8134571
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
 

Author Comment

by:borbjo
ID: 8134751
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
 

Author Comment

by:borbjo
ID: 8135065
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
 
LVL 9

Expert Comment

by:Ovi
ID: 8135125
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
 
LVL 9

Expert Comment

by:Ovi
ID: 8135178
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
 

Author Comment

by:borbjo
ID: 8135268
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
 
LVL 9

Expert Comment

by:Ovi
ID: 8135448
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
 

Author Comment

by:borbjo
ID: 8135707
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
 
LVL 9

Accepted Solution

by:
Ovi earned 750 total points
ID: 8135819
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
 

Author Comment

by:borbjo
ID: 8136009
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
 

Author Comment

by:borbjo
ID: 8158352
I didn't quite make it, but nevertheless your comments helped me along the way.
0
 
LVL 9

Expert Comment

by:Ovi
ID: 8158575
Thanks.
0

Featured Post

Get 15 Days FREE Full-Featured Trial

Benefit from a mission critical IT monitoring with Monitis Premium or get it FREE for your entry level monitoring needs.
-Over 200,000 users
-More than 300,000 websites monitored
-Used in 197 countries
-Recommended by 98% of users

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Go is an acronym of golang, is a programming language developed Google in 2007. Go is a new language that is mostly in the C family, with significant input from Pascal/Modula/Oberon family. Hence Go arisen as low-level language with fast compilation…
In this post we will learn different types of Android Layout and some basics of an Android App.
Viewers learn about the third conditional statement “else if” and use it in an example program. Then additional information about conditional statements is provided, covering the topic thoroughly. Viewers learn about the third conditional statement …
Video by: Michael
Viewers learn about how to reduce the potential repetitiveness of coding in main by developing methods to perform specific tasks for their program. Additionally, objects are introduced for the purpose of learning how to call methods in Java. Define …
Suggested Courses
Course of the Month13 days, 10 hours left to enroll

800 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question