package gve.calc.formula;

import java.awt.*;
import java.awt.event.*;
import awt.Button;

@author Geert Vernaeve
public class Fraction extends InfixBinaryOp { public String getName() { return "/"; } public int getPri() { return 1900; } public int getLeftPri() { return -10000; } public int getRightPri() { return -10000; } public Fraction(Part l,Part r) { super(l,r); } public Part evaluate(Evaluator ev) { Object t = left.evaluate(ev); Object b = right.evaluate(ev); if (t instanceof Real && b instanceof Real) return new Real(((Real)t).doubleValue / ((Real)b).doubleValue); else if (t instanceof Real && b instanceof Interval) { Interval i = (Interval)b; double top = ((Real)t).doubleValue; if (i.low < 0 && i.high > 0) { // Resultaat is unie van twee intervallen if (top == 0) return new Real(0); else if (top > 0) return new OperatorUnion( new Interval(Double.NEGATIVE_INFINITY,top/i.low,true,i.openLow), new Interval(top/i.high,Double.POSITIVE_INFINITY,i.openHigh,true)); // top < 0 else return new OperatorUnion( new Interval(Double.NEGATIVE_INFINITY,top/i.high,true,i.openHigh), new Interval(top/i.low,Double.POSITIVE_INFINITY,i.openLow,true)); } // Resultaat is een enkel interval return new Interval(top/i.low,top/i.high,i.openLow,i.openHigh); } else if (t instanceof Interval && b instanceof Real) { Interval i = (Interval)t; double bot = ((Real)b).doubleValue; return new Interval(i.low/bot,i.high/bot,i.openLow,i.openHigh); } else if (t instanceof Interval && b instanceof Interval) { Interval top = (Interval)t; Interval bot = (Interval)b; if (top.low >= 0) { // top is positive if (bot.low > 0) return new Interval(top.low/bot.high,top.high/bot.low, top.openLow||bot.openHigh,top.openHigh||bot.openLow); if (bot.high < 0) return new Interval(top.high/bot.high,top.low/bot.low, top.openHigh||bot.openHigh,top.openLow||bot.openLow); if (bot.low == 0) return new Interval(top.low/bot.high,Double.POSITIVE_INFINITY, top.openLow||bot.openHigh,true); if (bot.high == 0) return new Interval(Double.NEGATIVE_INFINITY,top.low/bot.low, true,top.openLow||bot.openLow); // else bot.low < 0 < bot.high return new OperatorUnion( new Interval(Double.NEGATIVE_INFINITY,top.low/bot.low,true,top.openLow||bot.openLow), new Interval(top.low/bot.high,Double.POSITIVE_INFINITY,top.openLow||bot.openHigh,true)); } else if (top.high <= 0) { // top is negative if (bot.low > 0) return new Interval(top.low/bot.low,top.high/bot.high, top.openLow||bot.openLow,top.openHigh||bot.openHigh); if (bot.high < 0) return new Interval(top.high/bot.low,top.low/bot.high, top.openHigh||bot.openLow,top.openLow||bot.openHigh); if (bot.high == 0) return new Interval(top.high/bot.low,Double.POSITIVE_INFINITY, top.openHigh||bot.openLow,true); if (bot.low == 0) return new Interval(Double.NEGATIVE_INFINITY,top.high/bot.high, true,top.openHigh||bot.openHigh); // else bot.low < 0 < bot.high return new OperatorUnion( new Interval(Double.NEGATIVE_INFINITY,top.high/bot.high,true,top.openHigh||bot.openHigh), new Interval(top.high/bot.low,Double.POSITIVE_INFINITY,top.openHigh||bot.openLow,true)); } else { // top contains both positive and negative values if (bot.low > 0) return new Interval(top.low/bot.low,top.high/bot.low, top.openLow||bot.openLow,top.openHigh||bot.openLow); if (bot.high < 0) return new Interval(top.high/bot.high,top.low/bot.high, top.openHigh||bot.openHigh,top.openLow||bot.openHigh); else return new Interval(Double.NEGATIVE_INFINITY,Double.POSITIVE_INFINITY,true,true); } } else return new Fraction(left,right); } public Component createView(FormulaView view) { return new FractionView(this,view); } public static Part read(java.io.BufferedReader r) throws java.io.IOException, ClassNotFoundException,NoSuchMethodException, java.lang.reflect.InvocationTargetException,IllegalAccessException { Part top = Part.read(r); Part bottom = Part.read(r); return new Fraction(top,bottom); } } class FractionView extends Container implements HasCursorPos,KeydnListener,View,MouseSensitive,CreatesPopup,ActionListener { private Fraction model;
Only valid if the FormulaView's cursorComp is this view. Possible values: Somewhere_LEFT, Somewhere_RIGHT, Somewhere
private int cursorPos; public FractionView(Fraction fr,FormulaView view) { model = fr; add(view.getView(fr.left)); add(new CursorPosLabel(model.getName())); add(view.getView(fr.right)); MVC.registerView(model,this); } public Object getModel() { return model; }
Called when "Invert" is chosen Note that we share this code with InfixBinaryOpView. So, maybe it's better to develop a Controller class hierarchy ...
public void updateView(Object with) { FormulaView view = FormulaView.get(this); Component lview = view.getView(model.left); Component rview = view.getView(model.right); remove(2); remove(0); add(lview,0); add(rview,2); validate(); }
Possible values: >=0 indicates that cursor is at operator name
public void setCursorPos(int pos) { if (pos >= 0) { getOperatorView().setCursorPos(pos); cursorPos = Somewhere; getComponent(1).setVisible(true); invalidate(); return; } switch (pos) { case Somewhere_LEFT: case Somewhere_RIGHT: cursorPos = pos; getOperatorView().deactivate(); getComponent(1).setVisible(false); invalidate(); break; default: getOperatorView().setCursorPos(pos); cursorPos = Somewhere; getComponent(1).setVisible(true); invalidate(); break; } }
Return the View of the operator itself (i.e. without the left and right arguments). This is our middle child. PENDING: We have this method also in InfixBinaryOpView. Someday, renice up the class hierarchy to get rid of the double implementation please.
HasCursorPos getOperatorView() { return (HasCursorPos)getComponent(1); } public int getCursorPos() { return 0; } public void deactivate() { invalidate(); getComponent(1).setVisible(false); } public void activate() {} public boolean mousePressed(int flags,int x,int y) { return false; } public void mouseDown(int flags,int x,int y) { Dimension top = getComponent(0).getPreferredSize(); if (y < top.height) return; Point botLoc = getComponent(2).getLocation(); if (y >= botLoc.y) return; FormulaView.get(this).setCursorComp(this, x < getSize().width/2 ? 0 : model.getName().length()); } public Dimension getMinimumSize() { return getPreferredSize(); } public Dimension getPreferredSize() { FormulaView view = FormulaView.get(this); Dimension top = getComponent(0).getPreferredSize(); Dimension bot = getComponent(2).getPreferredSize(); Dimension result = new Dimension(Math.max(top.width,bot.width) + 4,top.height + bot.height); if (view.getCursorComp() == this) { FontMetrics fm = getFontMetrics(getFont()); int spatwidth = fm.charWidth(' '); if (cursorPos == Somewhere) { // cursor at the fraction bar int strwidth = fm.stringWidth(model.getName()) + spatwidth; if (result.width < strwidth) result.width = strwidth; result.height += fm.getHeight(); } else { // cursor at the left or right of the fraction bar result.width += spatwidth; result.height += 3; } } else result.height += 3; return result; } public void doLayout() { Component t = getComponent(0); Component b = getComponent(2); t.validate(); b.validate(); Dimension top = t.getPreferredSize(); Dimension bot = b.getPreferredSize(); int width = Math.max(top.width,bot.width) + 4; int boty = top.height; // Y-pos of bottom child FormulaView view = FormulaView.get(this); int xshift = 0; if (view.getCursorComp() == this) { FontMetrics fm = getFontMetrics(getFont()); int spatwidth = fm.charWidth(' '); if (cursorPos == Somewhere) { // cursor at the fraction bar int strwidth = fm.stringWidth(model.getName()) + spatwidth; if (width < strwidth) width = strwidth; boty += fm.getHeight(); Component op = getComponent(1); Dimension opPref = op.getPreferredSize(); op.setBounds((width-opPref.width) / 2,top.height,opPref.width,opPref.height); } else { // cursor at the left or right of the fraction bar boty += 3; if (cursorPos == Somewhere_LEFT) xshift = spatwidth; } } else boty += 3; t.setBounds(xshift + (width-top.width) / 2,0,top.width,top.height); b.setBounds(xshift + (width-bot.width) / 2,boty,bot.width,bot.height); } public void paint(Graphics g) { super.paint(g); FormulaView view = FormulaView.get(this); int y = getComponent(0).getSize().height + 1; if (view.getCursorComp() == this) { if (cursorPos == Somewhere) { // cursor at the fraction bar return; // Nothing to do } FontMetrics fm = getFontMetrics(getFont()); int spatwidth = fm.charWidth(' '); int ascent = fm.getAscent(); int h = fm.getHeight(); if (cursorPos == Somewhere_LEFT) g.fillRect(0,y-ascent/2,spatwidth,h); else if (cursorPos == Somewhere_RIGHT) g.fillRect(getSize().width-spatwidth,y-ascent/2,spatwidth,h); } g.drawLine(1,y,getSize().width-1,y); } public boolean keydn(int key) { FormulaView view = FormulaView.get(this); switch (key) { case Key_UP: if (this == view.getCursorComp()) { view.setCursorComp(getComponent(0),Somewhere); return true; } else if (Part.isDescendantOr(view.getCursorPart(),model.right)) { view.setCursorComp(this,0); return true; } return false; case Key_DOWN: if (this == view.getCursorComp()) { view.setCursorComp(getComponent(2),Somewhere); return true; } else if (Part.isDescendantOr(view.getCursorPart(),model.left)) { view.setCursorComp(this,0); return true; } return false; case Key_LEFT: if (this == view.getCursorComp()) { if (cursorPos == Somewhere) { if (getOperatorView().getCursorPos() == 0) { setCursorPos(Somewhere_LEFT); return false; } break; // send to Controller } else if (cursorPos == Somewhere_RIGHT) { view.setCursorComp(this,model.getName().length()); return true; } else if (cursorPos == Somewhere_LEFT) { return false; // cannot move more to the left } } else { view.setCursorComp(this,Somewhere_LEFT); return true; } case Key_RIGHT: if (this == view.getCursorComp()) { if (cursorPos == Somewhere) { if (getOperatorView().getCursorPos() == model.getName().length()) { setCursorPos(Somewhere_RIGHT); return false; } break; // send to Controller } else if (cursorPos == Somewhere_LEFT) { view.setCursorComp(this,0); return true; } else if (cursorPos == Somewhere_RIGHT) { return false; // cannot move more to the right } } else { view.setCursorComp(this,Somewhere_RIGHT); return true; } case '(': if (this == view.getCursorComp()) { if (cursorPos != Somewhere) return false; // Surround this Fraction with brackets // Must use a temporary to keep model.parent for // the replace() { int cpos = getOperatorView().getCursorPos(); Part temp = new Identifier(); Part newpart = new Brackets(temp); Formula f = view.getFormula(); f.replace(model,newpart); f.replace(temp,model); view.setCursorPart(temp,cpos); } return true; } } if (cursorPos == Somewhere) // let the InfixBinaryOpController handle it return InfixBinaryOpController.keydn(key,this); else if (cursorPos == Somewhere_LEFT) return view.splitleft(model,key); else if (cursorPos == Somewhere_RIGHT) return view.splitright(model,key); return false; } public int getBaseline() { Component top = getComponent(0); FormulaView view = FormulaView.get(this); if (view.getCursorComp() == this) { if (cursorPos == Somewhere) { // cursor at the fraction bar FontMetrics fm = getFontMetrics(getFont()); return top.getSize().height + fm.getHeight()/2; } } return top.getSize().height + 1; } public Component createPopup(int x,int y) { awt.Button b; b = new awt.Button("Invert"); b.addActionListener(this); return b; } public void actionPerformed(ActionEvent evt) { String cmd = evt.getActionCommand(); if (cmd.equals("Invert")) { Part swap = model.left; model.left = model.right; model.right = swap; MVC.changed(model); } } }