summaryrefslogblamecommitdiffstats
path: root/dozentenmodul/src/main/java/org/openslx/dozmod/gui/control/table/ListTable.java
blob: 47062258aeb6ab88b335bc30080f8b98931cdf6e (plain) (tree)
1
2
3
4
5
6
7
8
9

                                             
                          
                           
                             
                            

                      
                        

                                      

                             

                                           
                                            
                                                  
                                           

                                          

                                        

                                                         


                                                   
                                         
 
                                                          
 
                                                                     
 

                                                   

                                                   

                                                      
         
 
                                                                                    
                        




                                                              
                                                             
                                                                      




                                                                                   
                                                                 
                                                        
                                                     



                                                    

                                                                  



                                                                              
                                                                                          
                                                                        
                                                                                      

                                                                           





                                                                     

         
                                                                                     
 
                                            
                                                                    



                                                              



                                                                     










                                                      



                                                
                                            
         

                                  

                                       

                                                                
 


























                                                                                 
                                                         
                                                   
                                    
                                         
                                 

                               






                                                                                    


                                                      
         
 









                                                                               
                 
                                                            
                              

         




                                        
           






                                                                                




                                                                                   
                                                                                 
                                                                  
                   
           
                                                                                                       
                                             
                                     
                                 










                                                                                    
                                                                                       






                                                                                                 


                                                                                                                               



                         

                              
                                                                     
 
                                                    
 
                                                 
 

                                                 
                                                                                  
                                           



                                                                              






                                                          
                                                                        





















                                                                                                                
                 












                                                                                    
                                                               

                         
 






                                                              
                                              



                                                      
                                                           


                         
                                                         
                                                            



                                                                         
                                                             

                                            

                                                                                           



                                                           









                                                             



                 


                                                                      

                                                                          




                                                                        


                                                                                                              



                                                                                                                             


                                        
         
 












                                                                                                                             
                                             
                                                

                                               
                                                          

                                                        
                                                          

                 

                                                                           

                 
                                                                                                         




                                                                                                                             


                                                             

                 

                                                                                      


                 
 
package org.openslx.dozmod.gui.control.table;

import java.awt.Component;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import javax.swing.Icon;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.RowSorter;
import javax.swing.SortOrder;
import javax.swing.event.RowSorterEvent;
import javax.swing.event.RowSorterListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableRowSorter;

import org.openslx.dozmod.gui.helper.TableColumnAdjuster;

@SuppressWarnings("serial")
public abstract class ListTable<T> extends JTable {

	private final ListModel<T> model;

	private final TableRowSorter<ListModel<T>> sorter;

	private List<RowSorter.SortKey> sortKeys = new ArrayList<>();

	private final TableColumnAdjuster adjuster;

	private final Comparator<T> itemComparator;

	public ListTable(ListTableColumn... columns) {
		this(null, columns);
	}

	public ListTable(Comparator<T> itemComparator, ListTableColumn... columns) {
		super();
		for (int i = 0; i < columns.length; i++) {
			TableColumn tc = new TableColumn(i);
			tc.setHeaderValue(columns[i].colName);
			addColumn(tc);
		}
		this.model = new ListModel<T>(this, columns);
		this.sorter = new TableRowSorter<ListModel<T>>(model);
		for (int i = 0; i < columns.length; ++i) {
			if (columns[i].sortComparator != null) {
				sorter.setComparator(i, columns[i].sortComparator);
			}
		}
		this.adjuster = new TableColumnAdjuster(this, 5);
		this.adjuster.setOnlyAdjustLarger(true);
		this.itemComparator = itemComparator;
		this.setModel(model);
		this.setRowSorter(sorter);
		this.setShowGrid(false);
		this.setCellSelectionEnabled(false);
		this.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
		this.getTableHeader().setReorderingAllowed(false);
		ListTableRenderer listTableRenderer = new ListTableRenderer();
		this.setDefaultRenderer(Object.class, listTableRenderer);
		this.setDefaultRenderer(Integer.class, listTableRenderer);
		this.setDefaultRenderer(Long.class, listTableRenderer);
		this.setDefaultRenderer(Boolean.class, getDefaultRenderer(Boolean.class));
		this.setDefaultRenderer(Icon.class, new IconRenderer());
		this.setDefaultEditor(Boolean.class, getDefaultEditor(Boolean.class));
		this.setRowSelectionAllowed(true);
		this.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		sorter.addRowSorterListener(new RowSorterListener() {
			@Override
			public void sorterChanged(RowSorterEvent e) {
				adjuster.adjustColumns();
			}
		});
	}

	protected abstract Object getValueAtInternal(T item, ListTableColumn column);

	public T getModelRow(int rowIndex) {
		if (rowIndex < 0 || rowIndex >= model.getRowCount())
			throw new IndexOutOfBoundsException();
		return model.data.get(rowIndex);
	}

	public T getViewRow(int rowIndex) {
		return getModelRow(convertRowIndexToModel(rowIndex));
	}

	public List<T> getSelectedItems() {
		int[] rows = getSelectedRows();
		if (rows.length == 0)
			return null;
		List<T> itemList = new ArrayList<T>();
		for (int i : rows) {
			itemList.add(getViewRow(i));
		}
		return itemList;
	}
	
	public T getSelectedItem() {
		int rowIndex = getSelectedRow();
		if (rowIndex == -1)
			return null;
		return getViewRow(rowIndex);
	}
	
	public List<T> getData() {
		if (model.data == null)
			return null;
		return Collections.unmodifiableList(model.data);
	}

	public boolean setSelectedItem(T item) {
		int selectionIndex = -1;
		for (int i = 0; i < model.data.size(); ++i) {
			T rowItem = model.data.get(i);
			if (itemComparator == null) {
				if (rowItem.equals(item)) {
					selectionIndex = i;
					break;
				}
			} else {
				if (itemComparator.compare(item, rowItem) == 0) {
					selectionIndex = i;
					break;
				}
			}
		}
		if (selectionIndex != -1) {
			selectionIndex = convertRowIndexToView(selectionIndex);
		}
		if (selectionIndex == -1) {
			getSelectionModel().clearSelection();
			return false;
		}
		setRowSelectionInterval(selectionIndex, selectionIndex);
		return true;
	}

	public void setData(List<T> data, boolean sort) {
		T oldSelection = getSelectedItem();
		model.setData(data);
		adjuster.adjustColumns();
		// handle sorting
		if (!sort)
			return;
		if (sortKeys.isEmpty()) {
			// sort by the first column by default
			sortKeys.add(new RowSorter.SortKey(0, SortOrder.ASCENDING));
			sorter.setSortKeys(sortKeys);
		}
		// always sort
		sorter.sort();
		if (oldSelection != null) {
			setSelectedItem(oldSelection);
		}
	}

	/**
	 * Show or hide the given column of the table
	 * 
	 * @param column
	 * @param visible
	 */
	public void setColumnVisible(ListTableColumn column, boolean visible) {
		model.setVisible(column, visible);
	}

	@Override
	public TableRowSorter<ListModel<T>> getRowSorter() {
		return sorter;
	}

	@Override
	public ListModel<T> getModel() {
		return model;
	}

	/**
	 * Callback when a cell is edited.
	 */
	public void setValueAt(Object newValue, T row, ListTableColumn column) {
		// Nothing by default
	}

	/**
	 * Called when rendering a column is being prepared. This is a good time to
	 * change the color or font for the given cell.
	 * 
	 * @param component The component representing the cell being rendered
	 * @param row item of the row being rendered
	 * @param listTableColumn column (model-based) of the cell being rendered
	 * @param isSelected whether the row is currently selected
	 * @return 
	 */
	public Component prepareRenderHook(Component component, T row, ListTableColumn listTableColumn,
			boolean isSelected) {
		// Nothing by default
		return component;
	}

	/**
	 * Called when the value of the given column needs to be transformed into a
	 * displayable item. By default this is the identity function, returning the
	 * value as-is.
	 * 
	 * @param value Value to render
	 * @param column Column index (model-based) being rendered
	 * @return Rendered version of value. This should match the column class
	 */
	public Object modelValueToDisplayFormat(Object value, ListTableColumn column) {
		return value;
	}

	@Override
	public final Component prepareRenderer(TableCellRenderer renderer, int row, int column) {
		Component c = super.prepareRenderer(renderer, row, column);
		T item = getViewRow(row);
		if (c != null && item != null) {
			c = prepareRenderHook(c, item, model.getColumn(convertColumnIndexToModel(column)), isRowSelected(row));
		}
		return c;
	}

	/**
	 * Model for our table
	 */
	public static class ListModel<T> extends AbstractTableModel {

		private final ColumnState[] columns;

		private ArrayList<T> data = null;

		private final ListTable<T> table;

		public ListModel(ListTable<T> table, ListTableColumn... columns) {
			this.table = table;
			this.columns = new ColumnState[columns.length];
			for (int i = 0; i < columns.length; ++i) {
				this.columns[i] = new ColumnState(columns[i]);
			}
		}

		public void setData(List<T> list) {
			this.data = new ArrayList<>(list);
			fireTableDataChanged();
		}

		public ListTableColumn getColumn(int modelColumnIndex) {
			return columns[modelColumnIndex].column;
		}

		public void setVisible(ListTableColumn column, boolean visible) {
			TableColumnModel cm = table.getColumnModel();
			for (int i = 0; i < columns.length; ++i) {
				ColumnState c = columns[i];
				if (c.column != column)
					continue;
				if ((c.hiddenColumn == null && visible) || (c.hiddenColumn != null && !visible))
					return;
				if (visible) {
					cm.addColumn(c.hiddenColumn);
					cm.moveColumn(cm.getColumnCount() - 1, c.oldIndex);
					c.hiddenColumn = null;
				} else {
					c.oldIndex = table.convertColumnIndexToView(i);
					c.hiddenColumn = cm.getColumn(c.oldIndex);
					cm.removeColumn(c.hiddenColumn);
				}
				break;
			}
		}
		
		@Override
		public boolean isCellEditable(int row, int col) {
			return columns[col].column.isEditable;
		}
		
		@Override
		public void setValueAt(Object aValue, int row, int col) {
			if (isCellEditable(row, col)) {
				T item = table.getModelRow(row);
				if (item == null)
					return;
				table.setValueAt(aValue, item, columns[col].column);
				fireTableCellUpdated(row, col);
			}
		}

		@Override
		public int getRowCount() {
			return data == null ? 0 : data.size();
		}

		@Override
		public int getColumnCount() {
			return columns.length;
		}

		@Override
		public String getColumnName(int col) {
			return columns[col].column.colName;
		}

		@Override
		public Class<?> getColumnClass(int col) {
			return columns[col].column.colClass;
		}

		@Override
		public Object getValueAt(int rowIndex, int columnIndex) {
			T item = table.getModelRow(rowIndex);
			if (item == null)
				return null;
			return table.getValueAtInternal(item, columns[columnIndex].column);
		}
		
		public T getModelRow(int rowIndex) {
			return table.getModelRow(rowIndex);
		}

		private static class ColumnState {
			int oldIndex;
			final ListTableColumn column;
			TableColumn hiddenColumn = null;

			ColumnState(ListTableColumn column) {
				this.column = column;
			}

		}

	}

	/**
	 * This renderer simply removes the default dotted border when
	 * a cell is selected
	 */
	private class ListTableRenderer extends DefaultTableCellRenderer {
		public ListTableRenderer() {
			putClientProperty("html.disable", Boolean.TRUE);
			putClientProperty("html", null);
		}

		@Override
		public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
				boolean hasFocus, int row, int column) {
			if (value != null) {
				value = modelValueToDisplayFormat(value, model.getColumn(convertColumnIndexToModel(column)));
			}
			super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
			setBorder(null);
			return this;
		}
	}

	private class IconRenderer extends DefaultTableCellRenderer {

		@Override
		public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
				boolean hasFocus, int row, int column) {
			if (value != null) {
				value = modelValueToDisplayFormat(value, model.getColumn(convertColumnIndexToModel(column)));
			}
			setIcon((Icon)value);
			return this;
		}
	}

	public static class ListTableColumn {
		public final boolean isEditable;
		public final Class<?> colClass;
		public final String colName;
		public final Comparator<?> sortComparator;

		public ListTableColumn(String colName) {
			this(colName, String.class, null);
		}

		public ListTableColumn(String colName, Class<?> colClass) {
			this(colName, colClass, null);
		}

		public ListTableColumn(String colName, Class<?> colClass, Comparator<?> sortComparator) {
			this(colName, colClass, sortComparator, false);
		}
		
		public ListTableColumn(String colName, Class<?> colClass, Comparator<?> sortComparator, boolean isEditable) {
			this.isEditable = isEditable;
			this.colName = colName;
			this.colClass = colClass;
			this.sortComparator = sortComparator;
		}

		public ListTableColumn(String colName, Comparator<?> sortComparator) {
			this(colName, String.class, sortComparator);
		}
	}

}