Downloads
| Java Timeline |
| Saturday, 21 June 2008 01:00 |
|
Description: A simple Java timeline component After searching the web for a timeline implementation in java with no success, I decided to create my own. It might not be the cream of the crop and maybe there are some nasty bugs but nevertheless I would like to share it with you. It is basically a modified JTable and looks like this:
You can download the timeline here. The sourcecode of the timeline: /* * A simple java timeline component * Copyright (C) 2008 Michael Netter * * 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. * */ import java.awt.Color; import java.awt.Component; import java.awt.Cursor; import java.awt.Dimension; import java.awt.Graphics; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionAdapter; import java.text.SimpleDateFormat; import java.util.ArrayList; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JLabel; import javax.swing.JTable; import javax.swing.ListSelectionModel; import javax.swing.SwingUtilities; import javax.swing.table.AbstractTableModel; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.TableCellRenderer; public class TimeLine extends JTable{ private final int ROWDISTANCE = 5; // the distance between the rows private final int DATEROWHEIGHT = 20; // height of the date row private ImageTableModel itm; // table model private int maxRowHeight = 20; // maximum row height private boolean autoSizeRowHeight; // resize row height automatically /** * Generates a new TimeLine object * @param rows - number of rows of the default grid * @param columns - number of columns of the default grid * @param autoSizeRowHeight - auto adjust row height to the max row height * @param autoSizeColumnWidth - auto adjust column width */ public TimeLine(int rows, int columns, boolean autoSizeRowHeight){ this.autoSizeRowHeight = autoSizeRowHeight; setTimeLineLookAndFeel(); itm = initModel(); itm.initGrid(rows, columns); setModel(itm); // set the table model } /** * Adds a new column to the table * @param date - The column name as java.util.Date * @param columndata - The row values for this column. * Object[0] contains the cell text, object[1] contains the ImageIcon */ public void addColumn(java.util.Date date, ArrayList<Object[]> columndata){ itm.addColumn(convertDate(date), columndata); // adjust row height if(autoSizeRowHeight){ calcMaxRowHeight(columndata); } } /** * Adds a new column to the table * @param columnName - The column name * @param columndata - The row values for this column. * Object[0] contains the cell text, object[1] contains the ImageIcon */ public void addColumn(String columnName, ArrayList<Object[]> columndata){ itm.addColumn(columnName, columndata); // adjust row height if(autoSizeRowHeight){ calcMaxRowHeight(columndata); } } /** * Utility function to set the scroll pane */ public int getRowCount(){ return itm.getRowCount(); } /** * Utility function to set the scroll pane */ public int getColumnCount(){ return itm.getColumnCount(); } /** * @return ImageTableModel - a new ImageTableModel */ private ImageTableModel initModel(){ return new ImageTableModel(); } /** * Set some JTable properties to make it * look more like a timeline */ private void setTimeLineLookAndFeel(){ this.getTableHeader().setReorderingAllowed(false); this.setCellSelectionEnabled(true); this.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); this.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); this.setIntercellSpacing(new Dimension(0,0)); this.setShowGrid(false); this.setTableHeader(null); this.setDefaultRenderer(Object.class, new MyCellRenderer(this)); this.addMouseMotionListener(new MyMouseMotionAdapter(this)); } /** * Converts a java.util.Date object to * a readable format * @param date - a java.util.Date object * @return String - Date as a String */ private String convertDate(java.util.Date date){ SimpleDateFormat df = new SimpleDateFormat("dd.MM.yyyy"); return df.format(date); } /** * Calculates the maximum row height * @param list - the column list */ private void calcMaxRowHeight(ArrayList<Object[]> list){ int max = 0; for(int i=0;i<list.size();i++){ int labelheight = 0; int imageheight = 0; if(list.get(i) != null){ if(list.get(i)[0] != null){ if(list.get(i)[0] instanceof String){ labelheight = new JLabel((String)list.get(i)[0]).getPreferredSize().height; } } if(list.get(i)[1] != null){ if(list.get(i)[1] instanceof ImageIcon){ imageheight = ((ImageIcon)list.get(i)[1]).getIconHeight(); } } } int height = labelheight + imageheight + ROWDISTANCE; if(maxRowHeight < height){ maxRowHeight = height; } } } @Override public void paintComponent(Graphics g){ super.paintComponent(g); g.setColor( Color.BLACK ); int y = getRowHeight(0)*(itm.getRowCount()-1) - 1; g.drawLine(0, y, getSize().width, y); } @Override public Component prepareRenderer(TableCellRenderer renderer, int row, int column){ Component c = super.prepareRenderer(renderer, row, column); if(row == itm.getRowCount()-1){ c.setBackground(java.awt.SystemColor.control); } else if( !this.isCellSelected(row, column)){ c.setBackground(column % 2 != 0 ? new Color(241, 245, 250) : null); } return c; } @Override /** * makes the date column look like a table header */ public void changeSelection(int row, int column, boolean toggle, boolean extend){ Object o = getValueAt(row, column); if (o != null && row != itm.getRowCount()-1){ super.changeSelection(row, column, toggle, extend); } } /** * MouseMotionAdapter for this table * Used to set the mouse cursor only */ class MyMouseMotionAdapter extends MouseMotionAdapter{ private TimeLine theTimeLine; public MyMouseMotionAdapter(TimeLine table){ this.theTimeLine = table; } public void mouseMoved(MouseEvent me) { int row = rowAtPoint( me.getPoint() ); if(row < theTimeLine.itm.getRowCount()-1){ theTimeLine.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); } else{ theTimeLine.setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); } } } /** * Cell renderer for this table */ class MyCellRenderer extends DefaultTableCellRenderer{ private TimeLine theTimeLine; public MyCellRenderer(TimeLine timeline){ this.theTimeLine = timeline; setHorizontalAlignment( CENTER ); setVerticalTextPosition(JLabel.BOTTOM); setHorizontalTextPosition(JLabel.CENTER); } public void setValue(Object value) { Object[] a = (Object[])value; if(value == null){ // empty cell setIcon(null); super.setValue(value); } else if (a[1] instanceof Icon) { // normal cell with text and image setIcon((Icon) a[1]); setText((String)a[0]); this.setAlignmentY(JLabel.BOTTOM_ALIGNMENT); theTimeLine.setRowHeight(maxRowHeight); theTimeLine.setRowHeight(itm.getRowCount()-1,DATEROWHEIGHT); //this.setBorder(new RoundBorder(Color.red,Color.green)); } else if(a[1] == null && a[0] instanceof String){ setText((String)a[0]); setIcon(null); } else { setIcon(null); super.setValue(value); } } } /** * The table model for this timeline * */ class ImageTableModel extends AbstractTableModel{ private ArrayList<String> columnnames; // holds the column names private ArrayList<ArrayList<Object[]>> data; // holds the table data private int maxRowCount; private int columnCursor; // points on the current column public ImageTableModel(){ columnnames = new ArrayList<String>(); data = new ArrayList<ArrayList<Object[]>>(); maxRowCount = 0; columnCursor = 0; } public Object getValueAt(int row, int column){ if (data.get(column).size()-1<row){ return null; } else{ return data.get(column).get(row); } } public int getRowCount(){ return maxRowCount; } public int getColumnCount(){ return columnnames.size(); } public String getColumnName( int columnIndex ){ return columnnames.get(columnIndex); } /** * Adds a new column to the table * @param columnName - The column name * @param columndata - The row values for this column. */ public void addColumn(String columnName, ArrayList<Object[]> columndata){ if(columnCursor >= columnnames.size()){ columnnames.add(columnName); data.add(rotateFillList(columnName,columndata)); } else{ columnnames.set(columnCursor, columnName); data.set(columnCursor, rotateFillList(columnName,columndata)); } SwingUtilities.invokeLater (new Runnable(){ // fixes a nasty java vector bug public void run () { fireTableStructureChanged(); fireTableDataChanged(); } }); columnCursor++; } public void initGrid(int rows, int columns){ for(int i=0;i<columns;i++){ ArrayList<Object[]> newdata = new ArrayList<Object[]>(); for(int j=0;j<rows;j++){ newdata.add(null); } columnnames.add(String.valueOf(i)); data.add(newdata); maxRowCount = rows; } SwingUtilities.invokeLater (new Runnable(){ // fixes a nasty java vector bug public void run () { fireTableStructureChanged(); fireTableDataChanged(); } }); } /** * Rotates the list. If list.size() is smaller than * maxRowCount the list if filled with null values * This generates the bottom up effect * @param columnName - The column name * @param list * @return list */ private ArrayList<Object[]> rotateFillList(String columnName, ArrayList<Object[]> list){ list.add(0,new Object[]{columnName,null}); // set column name to be on the bottom if(maxRowCount < list.size()){ // adjust all rows to the new maxRowCount maxRowCount = list.size(); for(int i=0;i<data.size();i++){ int diff = maxRowCount - data.get(i).size(); for(int j=0;j<diff;j++){ data.get(i).add(0,null); } } } else { // fill with null values int diff = maxRowCount - list.size(); for(int i=0;i<diff;i++){ list.add(null); } } ArrayList<Object[]> rotatedList = new ArrayList<Object[]>(); for(int i= list.size()-1;i>=0;i--){ // rotate list rotatedList.add(list.get(i)); } return rotatedList; } } }
|

It should be useful!