package gve.calc.formula;

import java.awt.*;
import gve.calc.formula.Brackets;

Cursor positions: -1 to the left of the part 0 at left delimiter, 1 at right delimiter, 2 to the right of the part @author Geert Vernaeve
public class Brackets extends Part { Part child; public static final byte BRACKET = 0; // () public static final byte CURLY = 1; // {} public static final byte SQUARE = 2; // [] public static final byte QUOTE = 3; // `' private byte kind; Brackets() { this (new Identifier()); } public Brackets(char c) { this(); switch (c) { case '(': kind = BRACKET; break; case '{': kind = CURLY; break; case '[': kind = SQUARE; break; case '\'': case '`': kind = QUOTE; break; default: throw new Error("Illegal char in Brackets constructor: "+c); } } public Brackets(Part ch) { this(ch,BRACKET); } public Brackets(Part ch,byte kind) { child = ch; ch.parent = this; this.kind = kind; } public Object clone() { return new Brackets((Part)child.clone(),kind); } public byte getKind() { return kind; } public boolean equals(Object obj) { if (!(obj instanceof Brackets)) return false; return child.equals(((Brackets)obj).child); } public Part evaluate(Evaluator ev) { switch (kind) { case BRACKET: return child.evaluate(ev); case QUOTE: return (Part)child.clone(); case CURLY: case SQUARE: return new Brackets(child.evaluate(ev),kind); default: return null; } } public void replaceChild(Part ch,Part byThat) { if (ch == child) { child = byThat; byThat.parent = this; } else System.out.println("Brackets: replaceChild() error"); } public String toString() { return "Brackets[kind="+kind+"]"; } public Part getChild() { return child; } public Part getChild(int count) { return count==0 ? child : null; } public void write(java.io.Writer w) throws java.io.IOException { super.write(w); w.write(kind+"\n"); child.write(w); } public static Part read(java.io.BufferedReader r) throws java.io.IOException, ClassNotFoundException,NoSuchMethodException, java.lang.reflect.InvocationTargetException,IllegalAccessException { byte kind = (byte)Integer.parseInt(r.readLine()); return new Brackets(Part.read(r),kind); } public boolean same(Object o) { switch (kind) { case BRACKET: return child.same(unbracket(o)); case CURLY: { o = unbracket(o); if (!(o instanceof Brackets)) return false; Brackets obj = (Brackets)o; if (obj.kind != CURLY) return false; return OperatorSet.subseteq(child,obj.child) && OperatorSet.subseteq(obj.child,child); } case SQUARE: case QUOTE: // PENDING default: return false; } } public static Object unbracket(Object obj) { if (obj instanceof Part) return unbracket((Part)obj); return obj; } public static Part unbracket(Part obj) { while (obj instanceof Brackets && ((Brackets)obj).kind==BRACKET) obj = ((Brackets)obj).child; return obj; } public Component createView(FormulaView view) { return new BracketsView(this,view); } public void saveLatex(java.io.BufferedWriter w) throws java.io.IOException { w.write("\\left"); switch (kind) { case BRACKET: w.write("("); break; case CURLY: w.write("\\{"); break; case SQUARE: w.write("\\["); break; case QUOTE: w.write("`"); break; } child.saveLatex(w); w.write("\\right"); switch (kind) { case BRACKET: w.write(")"); break; case CURLY: w.write("\\}"); break; case SQUARE: w.write("\\]"); break; case QUOTE: w.write("'"); break; } } }
Undo a remove bracket by surrounding the part by the appropriate bracket
class BracketsUndo extends UndoAction { private byte kind; private Part part; private Formula formula; public BracketsUndo(byte k,Part p,Formula f) { kind = k; part = p; formula = f; } public void undo() { Part dummy = new Identifier(); formula.replace(part,dummy); formula.replace(dummy,new Brackets(part,kind)); } } class BracketsView extends Container implements HasBaseline,KeydnListener,HasCursorPos,View,MouseSensitive { private Brackets model;
The child Components are laid out in a specific order.
public static final int LEFTCHILD = 0; public static final int RIGHTCHILD = 1; public static final int MIDDLECHILD = 2;
Values: 0 cursor left of left bracket; 1 at left bracket; 2 at right bracket; 3 right of right bracket, -1 no cursor
private int cursorPos; public BracketsView(Brackets br,FormulaView view) { model = br; add(new BracketView(true,model.getKind())); add(new BracketView(false,model.getKind())); add(view.getView(model.child)); cursorPos = -1; setLayout(new BracketsLayout()); } public Object getModel() { return model; } public void updateView(Object with) {} public void setCursorPos(int pos) { if (pos == Somewhere_LEFT) pos = 0; else if (pos == Somewhere_RIGHT) pos = 3; if (pos<0 || pos>3) pos = 1; //default fallback cursorPos = pos; invalidate(); BracketView l = (BracketView)getComponent(LEFTCHILD); BracketView r = (BracketView)getComponent(RIGHTCHILD); switch (cursorPos) { case 0: r.setViewType(BracketView.NORMAL); l.setViewType(BracketView.CURSOR); break; case 1: l.setViewType(BracketView.REVERSE); r.setViewType(BracketView.BOLD); break; case 2: r.setViewType(BracketView.REVERSE); l.setViewType(BracketView.BOLD); break; case 3: l.setViewType(BracketView.NORMAL); r.setViewType(BracketView.CURSOR); break; } repaint(); } public int getCursorPos() { return cursorPos; } public boolean mousePressed(int flags,int x,int y) { return false; } public void mouseDown(int flags,int x,int y) { //System.out.println("mousedown brackets "+x+" "+y); Component comp = getComponentAt(x,y); //System.out.println(comp+"\t"+getComponent(LEFTCHILD)+"\t"+getComponent(RIGHTCHILD)); //System.out.println(getComponent(LEFTCHILD).getBounds()+"\t"+getComponent(RIGHTCHILD).getBounds()); if (getComponent(LEFTCHILD) == comp) { FormulaView.get(this).setCursorComp(this,1); } else if (getComponent(RIGHTCHILD) == comp) { FormulaView.get(this).setCursorComp(this,2); } } public void deactivate() { cursorPos = -1; BracketView l = (BracketView)getComponent(LEFTCHILD); BracketView r = (BracketView)getComponent(RIGHTCHILD); l.setViewType(BracketView.NORMAL); r.setViewType(BracketView.NORMAL); } public void activate() {} public int getBaseline() { Component child = getComponent(MIDDLECHILD); if (child instanceof HasBaseline) { return ((HasBaseline)child).getBaseline(); } else return child.getPreferredSize().height / 2; } public boolean keydn(int key) { FormulaView view = FormulaView.get(this); switch (key) { case Key_LEFT: switch (cursorPos) { case 2: view.setCursorPart(model.child,Somewhere_RIGHT); invalidate(); return true; case 1: case 3: setCursorPos(cursorPos - 1); return true; case -1: view.setCursorComp(this,1); return true; } return false; case Key_RIGHT: switch (cursorPos) { case 1: view.setCursorPart(model.child,Somewhere_LEFT); invalidate(); return true; case 0: case 2: setCursorPos(cursorPos + 1); invalidate(); return true; case -1: view.setCursorComp(this,2); return true; } return false; case Key_BS: switch (cursorPos) { case 1: case 2: // cursor is on bracket itself => unbracket int oldpos = cursorPos; Formula f = view.getFormula(); f.replace(model,model.child); view.setCursorPart(model.child,oldpos==1 ? Somewhere_LEFT : Somewhere_RIGHT); view.addUndoAction(new BracketsUndo(model.getKind(),model.child,f)); return true; case 3: setCursorPos(2); invalidate(); return true; } } if (cursorPos == 0) return view.splitleft(model,key); else if (cursorPos == 3) return view.splitright(model,key); return false; } } class BracketsLayout implements LayoutManager {
The child Components are laid out in a specific order.
public static final int LEFTCHILD = 0; public static final int RIGHTCHILD = 1; public static final int MIDDLECHILD = 2; public BracketsLayout() {} public void addLayoutComponent(String name,Component comp) {} public void removeLayoutComponent(Component comp) {} public Dimension minimumLayoutSize(Container target) { return preferredLayoutSize(target); } public Dimension preferredLayoutSize(Container target) { Dimension l = target.getComponent(LEFTCHILD).getPreferredSize(); Dimension r = target.getComponent(RIGHTCHILD).getPreferredSize(); Dimension c = target.getComponent(MIDDLECHILD).getPreferredSize(); return new Dimension(l.width + c.width + r.width,c.height); } public void layoutContainer(Container target) { Component l = target.getComponent(LEFTCHILD); Component r = target.getComponent(RIGHTCHILD); Component c = target.getComponent(MIDDLECHILD); l.validate(); r.validate(); c.validate(); Dimension lDim = l.getPreferredSize(); Dimension rDim = r.getPreferredSize(); Dimension cDim = c.getPreferredSize(); l.setBounds(0,0,lDim.width,cDim.height); c.setBounds(lDim.width,0,cDim.width,cDim.height); r.setBounds(lDim.width + cDim.width,0,rDim.width,cDim.height); } }
Show a single bracket.
class BracketView extends Component { private boolean isLeft; private byte kind; public static final byte NORMAL = 0; public static final byte REVERSE = 1; public static final byte BOLD = 2; public static final byte CURSOR = 3; private byte viewtype; public BracketView(boolean isLeft,byte kind) { this.isLeft = isLeft; this.kind = kind; } public Dimension getPreferredSize() { int extrawidth; if (viewtype == CURSOR) extrawidth = getFontMetrics(getFont()).charWidth(' '); else if (viewtype == BOLD) extrawidth = 1; else extrawidth = 0; switch (kind) { case Brackets.BRACKET: case Brackets.CURLY: case Brackets.SQUARE: return new Dimension(extrawidth + 5,0); case Brackets.QUOTE: return new Dimension(extrawidth + getFontMetrics(getFont()).charWidth('`'),0); default: return null; } } public void setViewType(byte type) { viewtype = type; invalidate(); } private void drawLeftBracket(Graphics g,int xoffset,int yoffset) { Dimension siz = getSize(); int height = siz.height; switch (kind) { case Brackets.BRACKET: g.drawLine(xoffset+3,yoffset,xoffset,yoffset+3); g.drawLine(xoffset,yoffset+3,xoffset,yoffset+height-4); g.drawLine(xoffset,yoffset+height-4, xoffset+3,yoffset+height-1); break; case Brackets.CURLY: g.drawLine(xoffset+4,yoffset,xoffset+2,yoffset+3); g.drawLine(xoffset+2,yoffset+2,xoffset+2,yoffset+height/2-2); g.drawLine(xoffset+2,yoffset+height/2-2,xoffset,yoffset+height/2); g.drawLine(xoffset,yoffset+height/2,xoffset+2,yoffset+height/2+2); g.drawLine(xoffset+2,yoffset+height/2+2,xoffset+2,yoffset+height-3); g.drawLine(xoffset+2,yoffset+height-3, xoffset+4,yoffset+height-1); break; case Brackets.SQUARE: g.drawLine(xoffset+3,yoffset,xoffset,yoffset); g.drawLine(xoffset,yoffset,xoffset,yoffset+height-1); g.drawLine(xoffset+3,yoffset+height-1,xoffset,yoffset+height-1); break; case Brackets.QUOTE: g.drawString("`",xoffset,yoffset+g.getFontMetrics().getAscent()); break; } } private void drawRightBracket(Graphics g,int xoffset,int yoffset) { Dimension siz = getSize(); int height = siz.height; switch(kind) { case Brackets.BRACKET: xoffset += 5; g.drawLine(xoffset-4,yoffset,xoffset-1,yoffset+3); g.drawLine(xoffset-1,yoffset+3, xoffset-1,yoffset+height-4); g.drawLine(xoffset-1,yoffset+height-4, xoffset-4,yoffset+height-1); break; case Brackets.CURLY: xoffset += 5; g.drawLine(xoffset-5,yoffset,xoffset-3,yoffset+3); g.drawLine(xoffset-3,yoffset+2,xoffset-3,yoffset+height/2-2); g.drawLine(xoffset-3,yoffset+height/2-2,xoffset-1,yoffset+height/2); g.drawLine(xoffset-1,yoffset+height/2,xoffset-3,yoffset+height/2+2); g.drawLine(xoffset-3,yoffset+height/2+2,xoffset-3,yoffset+height-3); g.drawLine(xoffset-3,yoffset+height-3,xoffset-5,yoffset+height-1); break; case Brackets.SQUARE: xoffset += 5; g.drawLine(xoffset-4,yoffset,xoffset-1,yoffset); g.drawLine(xoffset-1,yoffset,xoffset-1,yoffset+height-1); g.drawLine(xoffset-4,yoffset+height-1,xoffset-1,yoffset+height-1); break; case Brackets.QUOTE: g.drawString("'",xoffset+g.getFontMetrics().charWidth('\''),yoffset+g.getFontMetrics().getAscent()); break; } } private void drawBracket(Graphics g,int x,int y) { if (isLeft) drawLeftBracket(g,x,y); else drawRightBracket(g,x,y); } public void paint(Graphics g) { switch (viewtype) { case REVERSE: g.setColor(getForeground()); { Dimension siz = getSize(); g.fillRect(0,0,siz.width,siz.height); } g.setColor(getBackground()); break; case BOLD: drawBracket(g,isLeft ? 1 : -1,0); break; case CURSOR: { FontMetrics fm = g.getFontMetrics(); int spatwidth = fm.charWidth(' '); int fonth = fm.getHeight(); Dimension siz = getSize(); if (isLeft) { g.fillRect(0,(siz.height-fonth)/2,spatwidth,fonth); drawBracket(g,spatwidth,0); return; } g.fillRect(siz.width-spatwidth,(siz.height-fonth)/2,spatwidth,fonth); } break; } drawBracket(g,0,0); } }