gt: 		double ratioVisible = rowCount / (double) listModel.getSize();
context: package gui;
  
  import gui.swing.ComponentFactory;
  import gui.swing.ComponentFactory.ClickableLabel;
  import gui.swing.TransparentViewPanel;
  
  import java.awt.BorderLayout;
  import java.awt.Component;
  import java.awt.Dimension;
  import java.awt.Font;
  import java.awt.event.ActionEvent;
  import java.awt.event.ActionListener;
  import java.awt.event.MouseAdapter;
  import java.awt.event.MouseEvent;
  import java.beans.PropertyChangeEvent;
  import java.beans.PropertyChangeListener;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.List;
  
  import javax.swing.DefaultListModel;
  import javax.swing.JButton;
  import javax.swing.JList;
  import javax.swing.JPanel;
  import javax.swing.JScrollPane;
  import javax.swing.SwingUtilities;
  import javax.swing.event.ListSelectionEvent;
  import javax.swing.event.ListSelectionListener;
  
  import main.ScreenSetup;
  import util.ArrayUtil;
  import util.ImageLoader;
  import cluster.Cluster;
  import cluster.ClusterController;
  import cluster.Clustering;
  import cluster.Clustering.SelectionListener;
  import cluster.ClusteringImpl;
  import cluster.Compound;
  
  public class CompoundListPanel extends JPanel
  {
  	JScrollPane listScrollPane;
  	MouseOverList list;
  	DefaultListModel<Compound> listModel;
  	DoubleNameListCellRenderer listRenderer;
  
  	boolean selfBlock = false;
  
  	Clustering clustering;
  	ClusterController clusterControler;
  	ViewControler viewControler;
  	GUIControler guiControler;
  
  	ClickableLabel clearSelectedButton;
  	JButton filterButton;
  
  	public CompoundListPanel(Clustering clustering, ClusterController clusterControler, ViewControler controler,
  			GUIControler guiControler)
  	{
  		this.clustering = clustering;
  		this.clusterControler = clusterControler;
  		this.viewControler = controler;
  		this.guiControler = guiControler;
  
  		buildLayout();
  
  		updateList();
  		installListeners(controler);
  	}
  
  	private void installListeners(final ViewControler controler)
  	{
  		clustering.addListener(new PropertyChangeListener()
  		{
  			@Override
  			public void propertyChange(PropertyChangeEvent evt)
  			{
  				if (evt.getPropertyName().equals(ClusteringImpl.CLUSTER_MODIFIED)
  						|| evt.getPropertyName().equals(ClusteringImpl.CLUSTER_CLEAR))
  				{
  					updateList();
  				}
  			}
  		});
  
  		clustering.addSelectionListener(new SelectionListener()
  		{
  
  			@Override
  			public void compoundWatchedChanged(Compound[] c)
  			{
  				if (selfBlock)
  					return;
  				selfBlock = true;
  				if (c.length == 0)
  					list.clearSelection();
  				else
  					list.setSelectedValue(c[0], true);
  				selfBlock = false;
  			}
  
  			@Override
  			public void compoundActiveChanged(Compound[] c)
  			{
  				if (!selfBlock)
  				{
  					selfBlock = true;
  					updateActiveCompoundSelection();
  					selfBlock = false;
  				}
  				updateClearButton();
  				updateFilterButton();
  			}
  
  			@Override
  			public void clusterWatchedChanged(Cluster c)
  			{
  				updateList();
  			}
  
  			@Override
  			public void clusterActiveChanged(Cluster c)
  			{
  				updateList();
  				updateClearButton();
  				updateFilterButton();
  			}
  		});
  
  		list.addMouseListener(new MouseAdapter()
  		{
  			public void mouseClicked(final MouseEvent e)
  			{
  				if (selfBlock)
  					return;
  				guiControler.block("click compound");
  				selfBlock = true;
  				viewControler.clearMouseMoveWatchUpdates(false);
  
  				Thread th = new Thread(new Runnable()
  				{
  					public void run()
  					{
  						try
  						{
  							int idx = list.getLastSelectedIndex();
  							Compound m = (Compound) listModel.elementAt(idx);
  							if (m == null)
  								throw new IllegalStateException();
  							if (e.isControlDown())
  								clusterControler.toggleCompoundActive(m);
  							else if (e.isShiftDown() && clustering.isCompoundActive())
  							{
  								int startIdx = listModel.indexOf(clustering.getActiveCompound());
  								int min, max;
  								if (startIdx < idx)
  								{
  									min = startIdx + 1;
  									max = idx;
  								}
  								else
  								{
  									min = idx;
  									max = startIdx - 1;
  								}
  								List<Compound> comps = new ArrayList<Compound>();
  								for (int i = min; i <= max; i++)
  									comps.add((Compound) listModel.elementAt(i));
  								for (Compound c : clustering.getActiveCompounds())
  									if (!comps.contains(c))
  										comps.add(c);
  								clusterControler.setCompoundActive(ArrayUtil.toArray(comps), true);
  							}
  							else
  							{
  								if (clustering.isCompoundActive(m))
  									clusterControler.clearCompoundActive(true);
  								else
  									clusterControler.setCompoundActive(m, true);
  							}
  
  						}
  						finally
  						{
  							selfBlock = false;
  							SwingUtilities.invokeLater(new Runnable()
  							{
  								public void run()
  								{
  									guiControler.unblock("click compound");
  								}
  							});
  						}
  					}
  				});
  				th.start();
  			}
  		});
  
  		list.addListSelectionListener(new ListSelectionListener()
  		{
  			@Override
  			public void valueChanged(ListSelectionEvent e)
  			{
  				if (selfBlock)
  					return;
  				selfBlock = true;
  				int index = list.getSelectedIndex();
  				if (index == -1)
  				{
  					//compoundWatched.clearSelection();
  				}
  				else
  				{
  					final Compound watched = (Compound) listModel.elementAt(index);
  					controler.doMouseMoveWatchUpdates(new Runnable()
  					{
  						public void run()
  						{
  							clusterControler.clearClusterWatched();
  							clusterControler.setCompoundWatched(watched);
  						}
  					});
  				}
  				selfBlock = false;
  			}
  		});
  
  		controler.addViewListener(new PropertyChangeListener()
  		{
  			@Override
  			public void propertyChange(PropertyChangeEvent evt)
  			{
  				if (evt.getPropertyName().equals(ViewControler.PROPERTY_COMPOUND_DESCRIPTOR_CHANGED)
  						|| evt.getPropertyName().equals(ViewControler.PROPERTY_HIGHLIGHT_CHANGED)
  						|| evt.getPropertyName().equals(ViewControler.PROPERTY_FEATURE_SORTING_CHANGED)
  						|| evt.getPropertyName().equals(ViewControler.PROPERTY_COMPOUND_FILTER_CHANGED))
  
  					updateList();
  				else if (evt.getPropertyName().equals(ViewControler.PROPERTY_SINGLE_COMPOUND_SELECTION_ENABLED))
  				{
  					updateList();
  					updateClearButton();
  					updateFilterButton();
  				}
  				else if (evt.getPropertyName().equals(ViewControler.PROPERTY_FONT_SIZE_CHANGED))
  					updateListSize();
  			}
  		});
  
  		guiControler.addPropertyChangeListener(new PropertyChangeListener()
  		{
  
  			@Override
  			public void propertyChange(PropertyChangeEvent evt)
  			{
  				if (evt.getPropertyName().equals(GUIControler.PROPERTY_VIEWER_SIZE_CHANGED))
  					updateListSize();
  			}
  		});
  
  		filterButton.addActionListener(new ActionListener()
  		{
  			@Override
  			public void actionPerformed(ActionEvent e)
  			{
  				clusterControler.applyCompoundFilter(ArrayUtil.toList(clustering.getActiveCompounds()), true);
  			}
  		});
  
  		clearSelectedButton.addActionListener(new ActionListener()
  		{
  			@Override
  			public void actionPerformed(ActionEvent arg0)
  			{
  				Thread noAwt = new Thread(new Runnable()
  				{
  					public void run()
  					{
  						if (clustering.isClusterActive() && clustering.getNumClusters() > 1
  								&& clustering.getActiveCluster().getCompounds().size() == 1)
  							clusterControler.clearClusterActive(true, true);
  						else if (clustering.isCompoundActive())
  							clusterControler.clearCompoundActive(true);
  						else if (clustering.isClusterActive() && clustering.getNumClusters() > 1)
  							clusterControler.clearClusterActive(true, true);
  						else if (viewControler.isSingleCompoundSelection())
  						{
  							viewControler.setSingleCompoundSelection(false);
  							SwingUtilities.invokeLater(new Runnable()
  							{
  								public void run()
  								{
  									updateList();
  									updateClearButton();
  								}
  							});
  						}
  					}
  				});
  				noAwt.start();
  			}
  		});
  	}
  
  	private void updateFilterButton()
  	{
  		filterButton.setVisible(listScrollPane.isVisible() && clustering.getActiveCompounds().length > 1
  				&& clustering.getActiveCompounds().length < clustering.getNumCompounds(false) - 1);
  	}
  
  	private void updateClearButton()
  	{
  		clearSelectedButton
  				.setVisible(listScrollPane.isVisible()
  						&& ((clustering.isClusterActive() && clustering.getNumClusters() > 1)
  								|| clustering.isCompoundActive() || viewControler.isSingleCompoundSelection()));
  	}
  
  	@SuppressWarnings("unchecked")
  	private void buildLayout()
  	{
  		listModel = new DefaultListModel<Compound>();
  
  		list = new MouseOverList(listModel);
  		list.setClearOnExit(false);
  
  		listRenderer = new DoubleNameListCellRenderer(listModel)
  		{
  			@Override
  			public void updateUI()
  			{
  				super.updateUI();
  				if (getFontLabel1() != null)
  				{
  					setFontLabel1(getFontLabel1().deriveFont((float) ScreenSetup.INSTANCE.getFontSize()));
  					setFontLabel2(getFontLabel2().deriveFont((float) ScreenSetup.INSTANCE.getFontSize()));
  					list.setFixedCellHeight(getRowHeight());
  				}
  			}
  
  			@SuppressWarnings("rawtypes")
  			public Component getListCellRendererComponent(JList list, Object value, int i, boolean isSelected,
  					boolean cellHasFocus)
  			{
  				super.getListCellRendererComponent(list, value, i, isSelected, cellHasFocus);
  
  				Compound c = (Compound) value;
  				setOpaque(isSelected || clustering.isCompoundActive(c));
  
  				setForeground(ComponentFactory.FOREGROUND);
  				if (clustering.isCompoundActive(c))
  				{
  					setBackground(ComponentFactory.LIST_ACTIVE_BACKGROUND);
  					setForeground(ComponentFactory.LIST_SELECTION_FOREGROUND);
  				}
  				else if (isSelected)
  				{
  					setBackground(ComponentFactory.LIST_WATCH_BACKGROUND);
  					setForeground(ComponentFactory.LIST_SELECTION_FOREGROUND);
  				}
  				else
  				{
  					setForegroundLabel2(c.getHighlightColorText());
  				}
  				return this;
  			}
  
  		};
  		listRenderer.setFontLabel2(listRenderer.getFontLabel2().deriveFont(Font.ITALIC));
  		list.setCellRenderer(listRenderer);
  		list.setOpaque(false);
  		list.setFocusable(false);
  
  		JPanel p = new JPanel(new BorderLayout(6, 6));
  		p.setOpaque(false);
  
  		listScrollPane = ComponentFactory.createViewScrollpane(list);
  		guiControler.registerSizeComponent(ComponentSize.COMPOUND_LIST, listScrollPane);
  		JPanel listWrapped = new TransparentViewPanel(new BorderLayout());
  		listWrapped.add(listScrollPane);
  		p.add(listWrapped);
  
  		clearSelectedButton = ComponentFactory.createCrossViewButton();
  		filterButton = ComponentFactory.createViewButton(ImageLoader.getImage(ImageLoader.Image.filter14_black),
  				ImageLoader.getImage(ImageLoader.Image.filter14));
  		filterButton.setName("filter-button");//for tests
  		JPanel buttonWrapped = new TransparentViewPanel(new BorderLayout(6, 6));
  		buttonWrapped.add(clearSelectedButton);
  		buttonWrapped.add(filterButton, BorderLayout.SOUTH);
  		JPanel removeButtonPanel = new JPanel(new BorderLayout());
  		removeButtonPanel.setOpaque(false);
  		removeButtonPanel.add(buttonWrapped, BorderLayout.NORTH);
  		p.add(removeButtonPanel, BorderLayout.EAST);
  		clearSelectedButton.setVisible(false);
  		filterButton.setVisible(false);
  
  		setLayout(new BorderLayout());
  		add(p);
  		setOpaque(false);
  	}
  
  	private void updateActiveCompoundSelection()
  	{
  		if (clustering.isCompoundActive())
  			list.setSelectedValue(clustering.getActiveCompound(), true);
  		else
  			list.clearSelection();
  	}
  
  	private void updateList()
  	{
  		if (!SwingUtilities.isEventDispatchThread())
  			throw new IllegalStateException("GUI updates only in event dispatch thread plz");
  
  		selfBlock = true;
  		setVisible(true);
  
  		setIgnoreRepaint(true);
  		if (clustering.getNumClusters() == 0)
  		{
  			listScrollPane.setVisible(false);
  			clearSelectedButton.setVisible(false);
  			filterButton.setVisible(false);
  		}
  		else if (!clustering.isClusterActive() && !viewControler.isSingleCompoundSelection())
  		{
  			listScrollPane.setVisible(false);
  		}
  		else
  		{
  			listModel.removeAllElements();
  
  			listScrollPane.setPreferredSize(null);
  			Compound m[] = new Compound[viewControler.isSingleCompoundSelection() ? clustering.getCompounds(false)
  					.size() : clustering.getActiveCluster().getCompounds().size()];
  
  			int i = 0;
  			for (Compound mod : (viewControler.isSingleCompoundSelection() ? clustering.getCompounds(false)
  					: clustering.getActiveCluster().getCompounds()))
  			{
  				m[i++] = mod;
  				if (mod.getDisplayName() == null)
  					throw new IllegalStateException("display name for compound is nil, check order of listeners");
  			}
  			for (Compound comp : (viewControler.isSingleCompoundSelection() ? clustering.getCompounds(false)
  					: clustering.getActiveCluster().getCompounds()))
  				comp.setFeatureSortingEnabled(true);//viewControler.isFeatureSortingEnabled());
  			for (Compound compound : m)
  				if (compound == null)
  					throw new IllegalStateException();
  			if (m.length > 1)
  				Arrays.sort(m);
  
  			for (Compound compound : m)
  				listModel.addElement(compound);
  			updateActiveCompoundSelection();
  			updateListSize();
  			listScrollPane.setVisible(true);
  		}
  		setIgnoreRepaint(false);
  		revalidate();
  		repaint();
  		selfBlock = false;
  	}
  
  	private void updateListSize()
  	{
  		int rowCount = (guiControler.getComponentMaxHeight(1) / listRenderer.getRowHeight()) / 3;
  
  