September 1, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Scaling Features in the JEditorPane Component

  • February 20, 2004
  • By Stanislav Lapitsky
  • Send Email »
  • More Articles »

Many text editors support a zooming feature. If we want to write our own editor based on javax.swing, we may need scaling, too. This article will explain how to add the feature to our text editor.

JEditorPane creates, edits, and represents all content via EditorKit, so first of all we should define our own EditorKit. SUN provides several EditorKits for different types of content. There are StyledEditorKit, RTFEditorKit, and HTMLEditorKit. For our example, we'll extend the simplest - StyledEditorKit.

Setting the Zoom Factor

For a start, we should set a zoom factor variable to let our component easily access it. The simplest way is to put it into the properties of the document:

scaledTextPane.getDocument().putProperty("ZOOM_FACTOR",
                                         new Double(2.5));

Thus, our zoom will be 250%.

EditorKit represents data using views that are generated by ViewFactory. We should replace the default ViewFactory so the only method we override in our EditorKit is getViewFactory().

class ScaledEditorKit extends StyledEditorKit {
  public ViewFactory getViewFactory() {
         return new StyledViewFactory();
  }
  class StyledViewFactory implements ViewFactory {

    public View create(Element elem) {
      String kind = elem.getName();
      if (kind != null) {
        if (kind.equals(AbstractDocument.ContentElementName)) {
          return new LabelView(elem);
        } else if
            (kind.equals(AbstractDocument.ParagraphElementName)) {
          return new ParagraphView(elem);
        } else if
            (kind.equals(AbstractDocument.SectionElementName)) {
          return new ScaledView(elem, View.Y_AXIS);
        } else if
            (kind.equals(StyleConstants.ComponentElementName)) {
          return new ComponentView(elem);
        } else if (kind.equals(StyleConstants.IconElementName)) {
          return new IconView(elem);
        }
      }

      // default to text display
      return new LabelView(elem);
    }

  }
}

ViewFactory provides views for different kinds of elements, but we should replace only the root view, the view for the document's root element. Each view draws content via the paint(Graphics g, Shape allocation) method that paints itself and invokes paint() methods of children. Thus, to provide correct drawing, we just need to set the scale factor to Graphics.

public double getZoomFactor() {
  Double scale=(Double)getDocument().getProperty("ZOOM_FACTOR");
  if (scale!=null) {
    return scale.doubleValue();
  }

  return 1;
}

public void paint(Graphics g, Shape allocation) {
  Graphics2D g2d = (Graphics2D)g;
  g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
                       RenderingHints.VALUE_FRACTIONALMETRICS_ON);
  double zoomFactor = getZoomFactor();
  AffineTransform old=g2d.getTransform();
  g2d.scale(zoomFactor, zoomFactor);
  super.paint(g2d, allocation);
  g2d.setTransform(old);
}

The Fractional metrics' rendered hint is used to provide more precise drawing of the editor pane's content. We should restore the previous AffineTransform after scaling because the same instance of Graphics is used to paint other components in the parent frame.

Changing the Layout Method

Now, our content becomes bigger but a part of it is invisible because view's size requirements correspond to the normal, not scaled, content. To change the size requirements, we override the getXXXSpan() methods. View lays out its content due to size requirements, so we have to change the layout method accordingly to preserve the default layout algorithm.

public float getMinimumSpan(int axis) {
       float f = super.getMinimumSpan(axis);
       f *= getZoomFactor();
       return f;
}

public float getMaximumSpan(int axis) {
       float f = super.getMaximumSpan(axis);
       f *= getZoomFactor();
       return f;
}

public float getPreferredSpan(int axis) {
       float f = super.getPreferredSpan(axis);
       f *= getZoomFactor();
       return f;
}

protected void layout(int width, int height) {
          super.layout(new Double(width /
            getZoomFactor()).intValue(),
            new Double(height * getZoomFactor()).intValue());
}

If we needed the read-only content, it would be enough, but of course our editor allows for making changes so we have to adapt it to provide the correct editing process. We should override two more methods: viewToModel() and modelToView(). viewToModel() defines which offset in the document's content corresponds to the specified point. We restore the method's parameters as though they were without scaling and invoke the super method of ancestor. modelToView(), in turn, provides Shape where the specified offset is placed in the view. All we need is to get super Shape and change it according to our zoom factor.

public Shape modelToView(int pos, Shape a, Position.Bias b)
             throws BadLocationException {
       double zoomFactor = getZoomFactor();
       Rectangle alloc;
       alloc = a.getBounds();
       Shape s = super.modelToView(pos, alloc, b);
       alloc = s.getBounds();
       alloc.x*=zoomFactor;
       alloc.y*=zoomFactor;
       alloc.width*=zoomFactor;
       alloc.height*=zoomFactor;

       return alloc;
}


public int viewToModel(float x, float y, Shape a,
                       Position.Bias[] bias) {
       double zoomFactor = getZoomFactor();
       Rectangle alloc = a.getBounds();
       x/=zoomFactor;
       y/=zoomFactor;
       alloc.x/=zoomFactor;
       alloc.y/=zoomFactor;
       alloc.width/=zoomFactor;
       alloc.height/=zoomFactor;

       return super.viewToModel(x, y, alloc, bias);
}




Page 1 of 2



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel