//This file is part of SECONDO. //Copyright (C) 2004, University in Hagen, Department of Computer Science, //Database Systems for New Applications. //SECONDO is free software; you can redistribute it and/or modify //it under the terms of the GNU General Public License as published by //the Free Software Foundation; either version 2 of the License, or //(at your option) any later version. //SECONDO is distributed in the hope that it will be useful, //but WITHOUT ANY WARRANTY; without even the implied warranty of //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //GNU General Public License for more details. //You should have received a copy of the GNU General Public License //along with SECONDO; if not, write to the Free Software //Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package viewer; import java.awt.geom.*; import java.awt.*; import javax.swing.*; import java.awt.event.*; import sj.lang.ListExpr; import java.util.*; import gui.SecondoObject; import gui.Environment; import java.io.*; import tools.Reporter; interface PNode{ void computeBounds(Graphics2D g); Rectangle2D getBounds(); void paint(Graphics2D g); boolean saveAsLatex(File f); } /** This class provides a datastructure for representing a Tree. * Using the paint method we can draw the tree on a graphic context. */ class TreeNode implements PNode{ /** Reads the Label of this Node from a nested list atom * **/ private boolean readLabel(ListExpr LE){ int type = LE.atomType(); if(type==ListExpr.INT_ATOM){ Label = ""+LE.intValue(); return true; } if(type==ListExpr.SYMBOL_ATOM){ Label = LE.symbolValue(); return true; } if(type==ListExpr.REAL_ATOM){ Label = ""+LE.realValue(); return true; } if(type== ListExpr.STRING_ATOM){ Label = LE.stringValue(); return true; } if(type==ListExpr.TEXT_ATOM){ Label = LE.textValue(); return true; } return false; } /** reads this (sub) tree from a nested list. * The return value descibes the sucess. * If the reading is unsuccessful, the node will be * a single leaf labeled with "Error". */ public boolean readFrom(ListExpr LE){ if(LE.atomType()!=ListExpr.NO_ATOM){ // a leaf if(!readLabel(LE)){ Reporter.debug("Error in reading Label 1"); Label = "Error"; sons = null; return false; } return true; } // now we have a list descibing a tree in the from (root subtree_1 .. subtree_n) // empty lists are not allowed here if(LE.listLength()==0){ Reporter.debug("Empty lists are not allowed as trees"); Label = "Error"; sons = null; return false; } ListExpr Root = LE.first(); // the root must be only a label if(!readLabel(Root)){ Reporter.debug("The root must be an atom"); Label = "Error"; sons = null; return false; } ListExpr Sons; if(LE.listLength()==2){ // sons are wrapped in a list Sons = LE.second(); } else{ // without a enclosing list Sons = LE.rest(); } if(Sons.atomType()!=ListExpr.NO_ATOM){ // a single leaf sons = new TreeNode[1]; sons[0] = new TreeNode("",null); sons[0].readFrom(Sons); }else{ // a list of sons if(Sons.isEmpty()){ // non sons in list sons = null; return true; } else{ // a proper list of sons sons = new TreeNode[Sons.listLength()]; for(int i=0;i}]%"); // save label as root out.println("{ \\Tr{\\psframebox[framearc=0.5]{"+Label+"}}}%"); // perform all sons if(sons!=null){ out.println("{%"); for(int i=0;imax){ max = d; } } if(!isLeaf){ for(int i=0;imax){ max = d; } } } return max; } public void paint(Graphics2D g){ if(!defined){ g.drawString("not defined",10,10); return; } else if(empty){ g.drawRect(0,0,10,10); g.drawLine(0,0,10,10); g.drawLine(0,10,10,0); } else { int entryWidth = determineEntryWidth(g.getFontMetrics()); paintRec(g, 0,0, entryWidth); } } private static void drawStringAt(Graphics2D g, String s, int x, int y){ int w = g.getFontMetrics().stringWidth(s); g.drawString(s, x - w/2 , y); } int paintRec(Graphics2D g, int x, int y, int entryWidth){ if(isLeaf){ int sep = 10; int w = (entryWidth + sep )* labels.length; // draw a rectangle g.drawRect(x,y, w, nodeHeight); int xl = 0; int xs = 0; // draw the entries and separators for(int i=0; i < labels.length;i++){ xl = x + i * (entryWidth+sep) + sep/2 + entryWidth/2; drawStringAt(g,labels[i], xl , y + nodeHeight -2); if(i < labels.length-1){ xs = x + (i+1) *(entryWidth+sep) ; g.drawLine(xs, y , xs, y + nodeHeight); } } return w; } else { int sep = 14; int w = (entryWidth + sep)* labels.length + sep -sep/3; // paint sons, store middles Vector v = new Vector(); int x1 = x; for(int i=0;i(); label = "--"; } public void computeBounds(Graphics2D g) { bounds = new Rectangle2D.Double(0,0, getWidth(g)+10, getHeight(g)+10); } int getWidth(Graphics2D g){ int myWidth = g.getFontMetrics().stringWidth(label) + 20; int sonWidth = 0; for(int i=0;i sws = new Vector(); // draw Subtrees for(int i=0; i < sons.size();i++){ int k = sons.get(i).paint(g, sw + x , y + nodeHeight + vSep); sws.add(new Integer(k)); sw = sw + k + nodeSep; } sw-=nodeSep; // paint node itself int ux; int uy; int dx = Math.max(0, sw - w)/2; g.drawRect(x+dx,y,w,nodeHeight); g.drawString(label, x+dx+10, y+nodeHeight-dy); ux = x + dx + w/2; uy = y + nodeHeight; // draw connections to sons int sy = y + nodeHeight + vSep; int startX = x; for(int i=0;i sons; private String label; private static final int nodeSep = 10; private static final int nodeHeight = 20; private static final int vSep = 20; } /** This class provides a panel drawing a single tree. * They are possibilities for magnifying or moving the drawing. */ class TreePainterPanel extends JPanel{ TreePainterPanel(){ super(); // add a mouselistener for magnifying the picture addMouseListener(new MouseAdapter(){ public void mouseClicked(MouseEvent evt){ int x = evt.getX(); int y = evt.getY(); int Bt = evt.getButton(); Dimension D = getSize(); if(Bt==MouseEvent.BUTTON1){ // left button = zoom in double sc = scalefactor; tmpAT.setTransform(scalefactor,0,0,scalefactor, -x*scalefactor+D.getWidth()/2, -y*scalefactor+D.getHeight()/2); AT.preConcatenate(tmpAT); repaint(); } if(Bt==MouseEvent.BUTTON3){ // right button = zoom out double sc = 1/scalefactor; tmpAT.setTransform(sc,0,0,sc, -x*sc+D.getWidth()/2, -y*sc+D.getHeight()/2); AT.preConcatenate(tmpAT); repaint(); } if(Bt==MouseEvent.BUTTON2){ // middle button = go to the first view firstPaint=true; repaint(); } } }); // change the background to be white setOpaque(true); setBackground(Color.WHITE); } /** Fits the tree to the available size */ public void fit(){ firstPaint=true; repaint(); } /** Zooms the current drawing by scale factor around the middle of the screen. */ public void zoomIn(){ double sc = scalefactor; Dimension D = getSize(); double x = D.getWidth()/2; double y = D.getHeight()/2; tmpAT.setTransform(scalefactor,0,0,scalefactor, -x*scalefactor+D.getWidth()/2, -y*scalefactor+D.getHeight()/2); AT.preConcatenate(tmpAT); repaint(); } /** Zooms out the current drawing */ public void zoomOut(){ double sc = 1/scalefactor; Dimension D = getSize(); double x = D.getWidth()/2; double y = D.getHeight()/2; tmpAT.setTransform(sc,0,0,sc, -x*sc+D.getWidth()/2, -y*sc+D.getHeight()/2); AT.preConcatenate(tmpAT); repaint(); } /** paints the contained tree **/ public void paint(Graphics g1){ Graphics2D g = (Graphics2D) g1; g.setBackground(Color.WHITE); super.paint(g1); if(Tree==null){ String S1 = "Secondo"; String S2 = "FernUni Hagen"; Rectangle2D BB = g.getFontMetrics().getStringBounds(S1,g); BB.setRect(0,0,-BB.getX()+BB.getWidth(),(-BB.getY()+BB.getHeight())); double sf = Math.min( (getSize().getWidth()-20)/BB.getWidth(), ((getSize().getHeight()/2)-20)/BB.getHeight()); AffineTransform OldTransform = g.getTransform(); AffineTransform A1 = new AffineTransform(); A1.setToIdentity(); A1.setToScale(sf,sf); g.transform(A1); Color OldColor = g.getColor(); g.setColor(Color.RED); g.drawString(S1,(int)(10/sf), (int)(getSize().getHeight() / (3*sf) )); g.setColor(Color.BLUE); g.setTransform(OldTransform); BB = g.getFontMetrics().getStringBounds(S2,g); BB.setRect(0,0,-BB.getX()+BB.getWidth(),(-BB.getY()+BB.getHeight())); g.rotate(0.5); g.scale(sf/2,sf/2); g.drawString(S2,(int)(20/sf+BB.getWidth()), (int)(-BB.getHeight()+getSize().getHeight() / (1.5*sf) )); g.setColor(OldColor); return; } g.setColor(Color.BLACK); // in the first paint we center the tree, possible fitting to the window if(firstPaint){ Dimension D = getSize(); Tree.computeBounds(g); TreeBounds=Tree.getBounds(); if(D!=null){ if(fit){ // move to center and fit to window double sc = Math.min((D.getWidth()-30)/TreeBounds.getWidth(), (D.getHeight()-20)/TreeBounds.getHeight()); double x = TreeBounds.getWidth()/2; double y = TreeBounds.getHeight()/2; AT.setTransform(sc,0,0,sc, -x*sc+D.getWidth()/2, -y*sc+D.getHeight()/2); } else{ // move to center AT.setToTranslation( (D.getWidth()-TreeBounds.getWidth())/2, (D.getHeight()-TreeBounds.getHeight())/2); } firstPaint=false; } } g.setTransform(AT); // set borderwidth to 1 undepending on the zoom factor g.setStroke(new BasicStroke((float) (1/AT.getScaleX()))); if(Tree!=null){ Tree.paint(g); } } /* move the drawing , values are in pixels*/ void moveXY(int X,int Y){ AffineTransform A = new AffineTransform(); A.setToTranslation(X,Y); AT.preConcatenate(A); repaint(); } // sets the tree to paint void setTree(PNode Tree){ this.Tree=Tree; AT.setToIdentity(); firstPaint=true; repaint(); } // the used affine transformation AffineTransform AT=new AffineTransform(); // temporal affine transformation to avoid creation of new objects AffineTransform tmpAT = new AffineTransform(); // scale for one single click double scalefactor = 1.5; // the Tree to paint PNode Tree=null; // the bounding box of this tree Rectangle2D TreeBounds=null; // paint without manual zoomung boolean firstPaint=true; // fit to window boolean fit=true; // Dimension for last paint Dimension lastDim = null; } /** This class is the Viewer for Trees */ public class TreeViewer extends SecondoViewer{ private void down(){ int d = (int) (TPP.getSize().getHeight() / 3); TPP.moveXY(0,-d); } private void up(){ int d = (int) (TPP.getSize().getHeight() / 3); TPP.moveXY(0,d); } private void left(){ int d = (int) (TPP.getSize().getWidth()/3); TPP.moveXY(d,0); } private void right(){ int d = (int) (TPP.getSize().getWidth()/3); TPP.moveXY(-d,0); } private static void addKeyListenerRec(KeyListener KL, Component C){ C.addKeyListener(KL); if(C instanceof Container){ Container F = (Container) C; Component[] Comps = F.getComponents(); for(int i=0;i0){ TPP.repaint(); } else if(evt.getKeyCode()==KeyEvent.VK_F && (evt.getModifiers() & KeyEvent.CTRL_MASK)>0){ TPP.fit(); } else if(evt.getKeyCode()==KeyEvent.VK_PLUS && (evt.getModifiers() & KeyEvent.CTRL_MASK)>0){ TPP.zoomIn(); } else if(evt.getKeyCode()==KeyEvent.VK_MINUS && (evt.getModifiers() & KeyEvent.CTRL_MASK)>0){ TPP.zoomOut(); } else if (evt.getKeyCode()==KeyEvent.VK_LEFT){ int d = (int) TPP.getSize().getWidth()/3; TPP.moveXY(-d,0); } else if (evt.getKeyCode()==KeyEvent.VK_RIGHT){ int d = (int) TPP.getSize().getWidth()/3; TPP.moveXY(d,0); } else if (evt.getKeyCode()==KeyEvent.VK_UP){ int d = (int) TPP.getSize().getHeight()/3; TPP.moveXY(0,-d); } else if (evt.getKeyCode()==KeyEvent.VK_DOWN){ int d = (int) TPP.getSize().getWidth()/3; TPP.moveXY(0,d); } } }; addKeyListenerRec(KL,this); } /** gets the name of this Viewer **/ public String getName(){ return "TreeViewer";} /** add an object to this viewer **/ public boolean addObject(SecondoObject o){ if(!canDisplay(o)){ return false; } ListExpr type = o.toListExpr().first(); ListExpr LE = o.toListExpr().second(); // we need only the value PNode pN; if(type.atomType()==ListExpr.SYMBOL_ATOM && (type.symbolValue().equals("tree") || isPMType(type))){ TreeNode N = new TreeNode("",null); if(isPMType(type)){ if(!N.readFromPMType(o.toListExpr())){ return false; } } else if(!N.readFrom(LE)){ // error in valueList return false; } pN = N; } else if(type.atomType()==ListExpr.SYMBOL_ATOM && (type.symbolValue().equals("text") )){ OpTreeNode op = new OpTreeNode(); if(!op.readFromString(LE.textValue())){ return false; } pN = op; } else { // b-tree BTreeNode bN = new BTreeNode(); if(!bN.readFrom(LE,0)){ return false; } pN = bN; } // values are correct Trees.add(pN); SObjects.add(o); ChoiceBox.addItem(o.getName()); ChoiceBox.setSelectedIndex(Trees.size()-1); requestFocus(); return true; } /** returns true if the type represents a periodic moving type **/ static boolean isPMType(ListExpr list){ if(list.atomType()!=ListExpr.SYMBOL_ATOM){ return false; } String v = list.symbolValue(); return v.equals("pmpoint") || v.equals("pmreal"); // extend if needed } /** removes an given object */ public void removeObject(SecondoObject o){ int index = SObjects.indexOf(o); if(index < 0) return; SObjects.removeElementAt(index); Trees.removeElementAt(index); ChoiceBox.removeItemAt(index); if(SObjects.size()==0){ TPP.setTree(null); } } public void removeAll(){ SObjects.clear(); Trees.clear(); ChoiceBox.removeAll(); TPP.setTree(null); } public boolean canDisplay(SecondoObject o){ ListExpr LE = o.toListExpr(); if(LE.listLength()!=2){ // not an secondo object (type value) return false; } ListExpr value = LE.second(); LE = LE.first(); // get the type // check for typed btree (btree (rel (...)) Attrname ) if(LE.atomType()==ListExpr.NO_ATOM){ if( LE.listLength()==3 && LE.first().atomType()==ListExpr.SYMBOL_ATOM && LE.first().symbolValue().equals("btree")){ return true; } } if(LE.atomType()!=ListExpr.SYMBOL_ATOM){ return false; } if(!LE.symbolValue().equals("tree") && !isPMType(LE) && !LE.symbolValue().equals("btree") && !LE.symbolValue().equals("text")){ return false; } if(LE.symbolValue().equals("text")){ return value.textValue().startsWith("(optree"); } return true; } public boolean isDisplayed(SecondoObject o){ return SObjects.indexOf(o)>=0; } public boolean selectObject(SecondoObject o){ int index = SObjects.indexOf(o); if(index <0) return false; ChoiceBox.setSelectedIndex(index); return true; } public MenuVector getMenuVector(){ return null; } public double getDisplayQuality(SecondoObject o){ if(canDisplay(o)) return 0.8; return 0; } private JComboBox ChoiceBox = new JComboBox(); private JButton SaveBtn= new JButton("LaTeX"); private JButton EPSButton = new JButton("EPS"); private JButton UpBtn=new JButton("^"); private JButton DownBtn = new JButton("v"); private JButton LeftBtn = new JButton("<");; private JButton RightBtn = new JButton(">"); private JButton ZoomInBtn = new JButton("Zoom In"); private JButton ZoomOutBtn = new JButton("Zoom Out"); private JButton FitBtn = new JButton("Fit"); private Vector SObjects = new Vector(10); private Vector Trees = new Vector(10); TreePainterPanel TPP = new TreePainterPanel(); }