package articles.multicolumn;
import java.awt.*;
import javax.swing.text.*;
public class MultiColumnView extends BoxView {
//default width for columns
int columnWidth=100;
//default height (could be changed to be JEditorPane's height
int columnHeight=100;
//children (paragraphs) offsets and spans and horizontal/vertical sizes
int majorTargetSpan=0;
int[] majorOffsets=null;
int[] majorSpans=null;
int minorTargetSpan=0;
int[] minorOffsets=null;
int[] minorSpans=null;
//starting positions of paragraph views
Point[] starts;
//cont of columns
int columnCount=1;
public MultiColumnView(Element elem, int axis) {
super(elem, axis);
}
protected void layout(int width, int height) {
columnHeight=height;
super.layout(columnWidth,height);
}
protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
super.layoutMajorAxis(targetSpan,axis,offsets,spans);
majorTargetSpan=targetSpan;
majorOffsets=offsets;
majorSpans=spans;
performMultiColumnLayout();
for (int i=0; i<offsets.length; i++) {
spans[i]=columnHeight;
offsets[i]=0;
}
}
protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
super.layoutMinorAxis(targetSpan,axis,offsets,spans);
minorTargetSpan=targetSpan;
minorOffsets=offsets;
minorSpans=spans;
performMultiColumnLayout();
for (int i=0; i<offsets.length; i++) {
View v=getView(i);
if (v instanceof MultiColumnParagraphView) {
MultiColumnParagraphView par=(MultiColumnParagraphView)v;
spans[i] = par.columnCount*columnWidth;
offsets[i]=par.columnNumber*columnWidth;
}
}
}
protected void performMultiColumnLayout() {
if (majorOffsets==null || minorOffsets==null || minorOffsets.length!=majorOffsets.length) {
return;
}
int childCount=majorOffsets.length;
int verticalStartOffset=0;
int columnNumber=0;
starts=new Point[childCount];
for (int i=0; i<childCount; i++) {
View v=getView(i);
starts[i]=new Point();
if (v instanceof MultiColumnParagraphView) {
MultiColumnParagraphView par=(MultiColumnParagraphView)v;
par.verticalStartOffset=verticalStartOffset;
par.columnWidth=columnWidth;
par.columnHeight=columnHeight;
par.columnNumber=columnNumber;
par.performMultiColumnLayout();
starts[i].y=verticalStartOffset;
starts[i].x=columnNumber*columnWidth;
verticalStartOffset=columnHeight-par.restHeight;
columnNumber+=par.columnCount-1;
}
}
columnCount = columnNumber + 1;
}
public float getPreferredSpan(int axis) {
if (axis==View.Y_AXIS) {
return columnHeight;
}
else {
return columnWidth*columnCount;
}
}
public float getMinimumSpan(int axis) {
if (axis==View.Y_AXIS) {
return columnHeight;
}
else {
return columnWidth*columnCount;
}
}
public float getMaximumSpan(int axis) {
if (axis==View.Y_AXIS) {
return columnHeight;
}
else {
return columnWidth*columnCount;
}
}
public int viewToModel(float x, float y, Shape a, Position.Bias[] bias) {
//define child container
if (starts!=null) {
for (int i=starts.length-1; i>0; i--) {
if ((starts[i].x<x && starts[i].y<y)
|| (starts[i].x+columnWidth<x)
){
return getView(i).viewToModel(x,y,a,bias);
}
}
}
return getView(0).viewToModel(x,y,a,bias);
}
public void paint(Graphics g, Shape a) {
super.paint(g,a);
Rectangle r=a.getBounds();
int shift=columnWidth;
g.setColor(new Color(240,240,240));
while (shift<r.width) {
g.drawLine(r.x+shift,r.y,r.x+shift,r.y+r.height);
shift+=columnWidth;
}
}
}
package articles.multicolumn;
import java.awt.*;
import javax.swing.text.*;
public class MultiColumnParagraphView extends ParagraphView {
protected int verticalStartOffset=0;
protected int columnCount=1;
protected int columnNumber=0;
protected int restHeight=0;
int[] majorOffsets=null;
int[] majorSpans=null;
int[] minorOffsets=null;
int[] minorSpans=null;
protected int columnWidth=100;
protected int columnHeight=100;
Point[] starts;
public MultiColumnParagraphView(Element elem) {
super(elem);
}
protected void layout(int width, int height) {
super.layout(columnWidth,0);
}
protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
super.layoutMajorAxis(targetSpan,axis,offsets,spans);
majorOffsets=offsets;
majorSpans=spans;
performMultiColumnLayout();
offsets=majorOffsets;
}
protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
super.layoutMinorAxis(targetSpan,axis,offsets,spans);
minorOffsets=offsets;
minorSpans=spans;
performMultiColumnLayout();
offsets=minorOffsets;
}
protected void performMultiColumnLayout() {
if (majorOffsets==null || minorOffsets==null || minorOffsets.length!=majorOffsets.length) {
return;
}
int childCount=majorOffsets.length;
int vo=verticalStartOffset;
int cc=1;
starts=new Point[childCount];
for (int i=0; i<childCount; i++) {
starts[i]=new Point();
if (vo+majorSpans[i]>columnHeight) {
cc++;
vo=0;
}
starts[i].y=vo;
starts[i].x=(columnNumber+cc-1)*columnWidth;
majorOffsets[i]=vo;
vo+=majorSpans[i];
restHeight=columnHeight-vo;
minorOffsets[i]=(cc-1)*columnWidth;
}
if (columnCount!=cc) {
columnCount = cc;
preferenceChanged(getView(0),true,true);
}
}
public float getPreferredSpan(int axis) {
if (axis==View.Y_AXIS) {
return columnHeight;
}
else {
return columnWidth*columnCount;
}
}
public float getMinimumSpan(int axis) {
if (axis==View.Y_AXIS) {
return columnHeight;
}
else {
return columnWidth*columnCount;
}
}
public float getMaximumSpan(int axis) {
if (axis==View.Y_AXIS) {
return columnHeight;
}
else {
return columnWidth*columnCount;
}
}
public int viewToModel(float x, float y, Shape a, Position.Bias[] bias) {
int ind=getViewIndexAtPoint((int)x,(int)y,a.getBounds());
View v=getViewAtPoint((int)x,(int)y,a.getBounds());
Shape childAlloc=getChildAllocation(ind,a);
return v.viewToModel(x,y,childAlloc,bias);
}
protected int getViewIndexAtPoint(int x, int y, Rectangle alloc) {
if (starts!=null) {
for (int i=starts.length-1; i>0; i--) {
if ((starts[i].x<x && starts[i].y<y)
|| (starts[i].x+columnWidth<x)){
return i;
}
}
}
return 0;
}
protected View getViewAtPoint(int x, int y, Rectangle alloc) {
if (starts!=null) {
for (int i=starts.length-1; i>0; i--) {
if ((starts[i].x<x && starts[i].y<y)
|| (starts[i].x+columnWidth<x)){
return getView(i);
}
}
}
return getView(0);
}
public Shape getChildAllocation(int index, Shape a) {
Rectangle r=super.getChildAllocation(index,a).getBounds();
r.x=starts[index].x+3;
r.y=starts[index].y+3;
return r;
}
}
package articles.multicolumn;
import javax.swing.*;
import javax.swing.text.*;
public class Application extends JFrame {
JEditorPane edit=new JEditorPane();
public Application() {
super("Multicolumn text example");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
edit.setEditorKit(new MultiColumnEditorKit());
this.getContentPane().add(new JScrollPane(edit));
this.setSize(200,200);
}
public static void main(String[] args) {
Application m = new Application();
m.setVisible(true);
}
}
class MultiColumnEditorKit extends StyledEditorKit {
ViewFactory defaultFactory=new MultiColumnFactory();
public ViewFactory getViewFactory() {
return defaultFactory;
}
}
class MultiColumnFactory 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 MultiColumnParagraphView(elem);
} else if (kind.equals(AbstractDocument.SectionElementName)) {
return new MultiColumnView(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);
}
}