/* * ConcreteSplitViewer program for analazing splits. * Copyright (C) 2006-2007 Mytinski Leonid (Leonid.Mytinski@gmail.com) * * This program 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. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ /* * SecondBestSplitViewer.java * * Created on 30 Июнь 2006 г., 0:17 */ package ru.concretesoft.concretesplitviewer; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Dimension; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import javax.swing.JPanel; import javax.swing.event.ListDataEvent; import javax.swing.event.ListDataListener; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import ru.spb.ConcreteSoft.tipWindow.MouseMoveQueue; import ru.spb.ConcreteSoft.tipWindow.TipWindow; /** * * @author Mytinski Leonid * * Panel for viewing splits relatively second best athlete * Панель для отображения сплитов в виде относительно второго лучшего */ public class SecondBestSplitViewer extends javax.swing.JPanel implements SplitViewer,ListDataListener,ListSelectionListener, MouseListener, MouseMotionListener{ private AthleteListModel aModel; private Time[] secondBest; private boolean draw = false; private int[] xCoord; private int otst; private List<int[]> yCoord; private int[] prom; private int yMax; private TipWindow tipWindow; private TipThreadSplitViewer tipThread; private int yMin; private List<XCoordinatesListener> listeners; private AthleteIcon editingAthlete; private int editingCPNumber; private GlassSecondBestViewPanel glassPane; private int yLocationOfStartDrag; /** Creates new form SecondBestSplitViewer */ public SecondBestSplitViewer() { editingAthlete = null; tipWindow = new TipWindow(); initComponents(); otst = -1; addMouseMotionListener(MouseMoveQueue.getInstance()); addMouseMotionListener(this); addMouseListener(this); listeners = new LinkedList<XCoordinatesListener>(); glassPane = new GlassSecondBestViewPanel(); // tipWindow = new TipWindow(); //// tipWindow.setVisible(true); // TipThreadSplitViewer tipThread = new TipThreadSplitViewer(tipWindow, this); // tipThread.start(); } /** This method is called from within the constructor to * initialize the form. * WARNING: Do NOT modify this code. The content of this method is * always regenerated by the Form Editor. */ // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents private void initComponents() { setLayout(new java.awt.GridBagLayout()); }// </editor-fold>//GEN-END:initComponents // Variables declaration - do not modify//GEN-BEGIN:variables // End of variables declaration//GEN-END:variables @Override public void paintComponent(Graphics g){ Graphics2D g2 = (Graphics2D)g; Dimension d = getSize(); g2.setPaint(Color.WHITE); g2.fillRect(0,0,d.width,d.height); g2.setPaint(Color.BLACK); FontMetrics fM = g2.getFontMetrics(); otst = (otst==-1)? fM.stringWidth("-000:00")+5: otst; int width = d.width-otst; int height = d.height-2; Distance dist = aModel.getDistance(); AthleteIcon[] athletes = (AthleteIcon[])(aModel.getSelectedValues()); int[] splits = aModel.getViewingSplits(); if(draw){ int[] sizes = new int[splits.length]; int tL = 0; int totalLength = 0; prom = new int[sizes.length]; for(int i=0;i<sizes.length;i++){ tL+=secondBest[splits[i]-1].getTimeInSeconds(); prom[i]=tL; totalLength+=dist.getLengthOfDist(splits[i]); } int x=otst; xCoord = new int[sizes.length]; for(int i=0;i<sizes.length;i++){ sizes[i] = (int)(((double)dist.getLengthOfDist(splits[i])/totalLength)*width); g2.drawLine(x+sizes[i],0,x+sizes[i],height); // String s; // if(i<=(splits.length-2)){ // int diff = splits[i+1]-splits[i]; // if(diff>1) // s = splits[i]+"-"+(splits[i+1]-1); // else // s=splits[i]+""; // } else { // if(splits[i]==dist.getNumberOfCP()) // s=java.util.ResourceBundle.getBundle("ru/concretesoft/concretesplitviewer/i18on").getString("Finish"); // else // s = splits[i]+""; // } // g2.drawString(s,x+sizes[i]-fM.stringWidth(s)/2,height+fM.getHeight()+1); x+=sizes[i]; xCoord[i] = x; } calculateYCoord(); int h = yMax-yMin; int hTime = fM.getHeight(); double stepTime = 30.0; int nT =(int) (h / stepTime); while(nT == 0){ stepTime = Math.round(stepTime/2); nT = (int)(h / stepTime); } while((height/nT)<(hTime+10)){ stepTime*=2; nT = (int) (h / stepTime); } Time tmp = new Time(0,2); tmp.setTimeInSeconds(Math.abs(yMin)); String s = tmp.getTimeString(); int y0 = (int)(((-yMin)/(double)h)*height); if(yMin<0) s = "-"+tmp.getTimeString(); else if(yMin>0) s = "+"+tmp.getTimeString(); else s = tmp.getTimeString(); g2.setPaint(Color.BLACK); g2.drawString(s,otst-fM.stringWidth(s),hTime-1); float dash1[] = {10.0f}; BasicStroke dashed = new BasicStroke(1.0f, java.awt.BasicStroke.CAP_BUTT, java.awt.BasicStroke.JOIN_MITER, 10.0f, dash1, 0.0f); g2.setStroke(dashed); for(int i=1;i<=nT;i++){ int timeCur = yMin + (Math.abs(yMin) % (int)stepTime) + (int)(stepTime*i); tmp.setTimeInSeconds(Math.abs(timeCur)); if(timeCur<0){ s = "-"+tmp.getTimeString(); } else if(timeCur>0){ s = "+"+tmp.getTimeString(); } else s = tmp.getTimeString(); int yH = (int)((timeCur-yMin)/(double)h*height); // yH = (int)((stepTime*i / h)*height); g2.setPaint(Color.BLACK); g2.drawString(s,otst-fM.stringWidth(s),yH); g2.setPaint(Color.LIGHT_GRAY); g2.drawLine(otst,yH,otst + width,yH); } g2.setStroke(new BasicStroke(1.0f)); g2.setPaint(Color.LIGHT_GRAY); g2.drawLine(otst,y0,xCoord[xCoord.length-1],y0); Iterator<int[]> it = yCoord.iterator(); int k=0; while(it.hasNext()){ g2.setPaint(athletes[k].getColor()); int[] yC = it.next(); int y = (int)(((yC[0]-yMin)/(double)h)*height); g2.drawLine(otst,y0,xCoord[0],y); for(int i=1;i<splits.length;i++){ int y2 = (int)(((yC[i]-yMin)/(double)h)*height); g2.drawLine(xCoord[i-1],y,xCoord[i],y2); // System.out.println(xCoord[i-1]+" "+y+" "+xCoord[i]+" "+y2); y=y2; } k++; } }else{ xCoord=null; } for(XCoordinatesListener e: listeners){ e.xCoordinatesChanged(this); } } @Override public String toString(){ return java.util.ResourceBundle.getBundle("ru/concretesoft/concretesplitviewer/i18on").getString("Second_Best_View"); } /** * Method check all parametrs needed for draw chart * */ private void setDraw(){ draw = (secondBest != null) && (aModel.getViewingSplits() != null) && (aModel.getSelectedValues().length != 0); } public void removeSplit(int x) { if(xCoord!=null){ for(int i=0;i<xCoord.length;i++){ if(x<xCoord[i]){ aModel.removeSplitsForN(aModel.getViewingSplits()[i]); break; } } } } /** * Method calculate all y coordinates of chart for all selected athletes * and add they to <code>ArrayList yCoord</code>. Also finds maximum and * minimum of all calculated y coordinates and store they to <code>yMax</code> * and <code>yMin</code> fields. * * */ private void calculateYCoord(){ AthleteIcon[] athletes = (AthleteIcon[])(aModel.getSelectedValues()); yMax = 0; yCoord = new ArrayList<int[]>(); yMin = 0; for(int i=0;i<athletes.length;i++){ Athlete a = athletes[i].getAthlete(); treatmentOneAthlete(a); } } /** * Method calculates all y coordinates of chart for one athlete * and add they to <code>List yCoord</code> * * @param a athlete whose chart would be showed */ private void treatmentOneAthlete(Athlete a){ yMax--; yMin++; int[] splits = aModel.getViewingSplits(); if (yCoord==null) yCoord = new ArrayList<int[]>(); else; yCoord.add(new int[splits.length]); yCoord.get(yCoord.size() - 1)[0] = a.getLap(splits[0]).getTimeInSeconds()-prom[0]; if(yCoord.get(yCoord.size() - 1)[0]<yMin)yMin=yCoord.get(yCoord.size() - 1)[0]; else if(yCoord.get(yCoord.size() - 1)[0]>yMax)yMax=yCoord.get(yCoord.size() - 1)[0]; else; int tot = 0; for(int i = 0;i<splits.length;i++){ tot += a.getLap(splits[i]).getTimeInSeconds(); yCoord.get(yCoord.size() - 1)[i] = tot - prom[i]; if(yCoord.get(yCoord.size() - 1)[i]<yMin)yMin=yCoord.get(yCoord.size() - 1)[i]; if(yCoord.get(yCoord.size() - 1)[i]>yMax)yMax=yCoord.get(yCoord.size() - 1)[i]; } yMax++; yMin--; } public void setModel(AthleteListModel aM){ aModel = aM; aM.addListDataListener(this); aM.addListSelectionListener(this); } public AthleteListModel getModel(){ return aModel; } public void intervalAdded(ListDataEvent e) { contentsChanged(e); } public void intervalRemoved(ListDataEvent e) { contentsChanged(e); } public void contentsChanged(ListDataEvent e) { // calculateYCoord(); if(aModel.getDistance()==null){ secondBest = null; }else{ Object[] allSelected = aModel.getGroupsList().getSelectedValues(); List<Group> groups = new ArrayList<Group>(); for(Object g : allSelected) groups.add((Group)g); secondBest = Tools.getAnyBest(groups,2); } setDraw(); repaint(); } public void valueChanged(ListSelectionEvent e) { // calculateYCoord(); setDraw(); repaint(); } public int getSplit(Point p){ if(xCoord!=null){ for(int i=0;i<xCoord.length; i++){ if(p.getX()<xCoord[i]){ return aModel.getViewingSplits()[i]; }else; } }else{ return -1; } return -1; } public void mouseClicked(MouseEvent evt) { if((evt.getButton()==MouseEvent.BUTTON2)||(MouseEvent.getMouseModifiersText(evt.getModifiers()).equals("Shift+Button1"))){ aModel.restoreAllSplits(); } else if(evt.getButton()==MouseEvent.BUTTON1){ removeSplit(evt.getX()); } } public void mousePressed(MouseEvent e) { //Find near node int x = e.getX(); int y = e.getY(); for(int i = 0; i < xCoord.length; i++){ // System.out.println(x+" "+xCoord[i]); if(Math.abs(xCoord[i]-x) < 5){// 5 horizontal points on both sides editingCPNumber = i;// index(only from viewing) of control point which would be edited double scale = (yMax - yMin) / (double)getSize().height; AthleteIcon[] selectedAthletes = (AthleteIcon[]) aModel.getSelectedValues(); for(int j = 0; j < yCoord.size(); j++){ int yA = (int) ((yCoord.get(j)[i] - yMin) / scale); // System.out.println(y+" "+yA); if(Math.abs(y-yA)<2){// 2 vertical points on both sides yLocationOfStartDrag = yA;// Store y coordinate(of node) of start dragging editingAthlete = selectedAthletes[j];// set editing athlete // put all needed parameters to glass panel glassPane.setAthlete(editingAthlete); glassPane.setViewingSplits(aModel.getViewingSplits()); glassPane.setCPsNumber(editingCPNumber); glassPane.setScale(scale); glassPane.setXCoord(xCoord); glassPane.setYCoord(yCoord.get(j)); glassPane.setOtst(otst); glassPane.setYMin(yMin); glassPane.setVisible(true); glassPane.setLocationOnScreen(this.getLocationOnScreen()); glassPane.setYLocation(y); // System.out.println(editingAthlete.getAthlete().getFamilyName()); break; } } break; } else if(x < xCoord[i]){ break; } } } public void mouseReleased(MouseEvent e) { glassPane.setVisible(false); glassPane.setAthlete(null); if(editingAthlete == null){ return ; } double scale = (yMax - yMin) / (double)getSize().height; editingCPNumber = aModel.getViewingSplits()[editingCPNumber];// change to real cp's number Time oldTime = editingAthlete.getAthlete().getLap(editingCPNumber); int diff = e.getY()-yLocationOfStartDrag; int diffInSec = (int)(diff*scale); Time newTime = new Time(0, 2); newTime.setTimeInSeconds(oldTime.getTimeInSeconds()+diffInSec); editingAthlete.getAthlete().setTimeOnLap(newTime, editingCPNumber); editingAthlete = null; editingCPNumber = 0; } public void mouseEntered(MouseEvent e) { tipThread = new TipThreadSplitViewer(tipWindow, this); tipThread.start(); } public void mouseExited(MouseEvent e) { tipThread.finish(); } public int[] getXCoordinatesOfLaps(){ return xCoord; } public void addXCoordinatesListener(XCoordinatesListener listener) { listeners.add(listener); } public void removeXCoordinatesListener(XCoordinatesListener listener) { listeners.remove(listener); } public void mouseDragged(MouseEvent e) { if((editingAthlete == null)||(glassPane==null)){ return ; } glassPane.setYLocation(e.getY()); } public void mouseMoved(MouseEvent e) { } public void setGlassPane(GlassSecondBestViewPanel tP){ glassPane = tP; } public JPanel getGlassPane() { return glassPane; } }