Index: AE/installer2/src/net/oni2/aeinstaller/gui/modtable/ModSelectionListener.java
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/gui/modtable/ModSelectionListener.java	(revision 631)
+++ AE/installer2/src/net/oni2/aeinstaller/gui/modtable/ModSelectionListener.java	(revision 631)
@@ -0,0 +1,18 @@
+package net.oni2.aeinstaller.gui.modtable;
+
+import net.oni2.aeinstaller.backend.mods.Mod;
+
+/**
+ * @author Christian Illy
+ */
+public interface ModSelectionListener {
+	/**
+	 * Called on changes to the mod selection
+	 * 
+	 * @param source
+	 *            Source of event
+	 * @param mod
+	 *            New selected mod
+	 */
+	public void modSelectionChanged(ModTable source, Mod mod);
+}
Index: AE/installer2/src/net/oni2/aeinstaller/gui/modtable/ModTable.java
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/gui/modtable/ModTable.java	(revision 631)
+++ AE/installer2/src/net/oni2/aeinstaller/gui/modtable/ModTable.java	(revision 631)
@@ -0,0 +1,238 @@
+package net.oni2.aeinstaller.gui.modtable;
+
+import java.awt.Desktop;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.ResourceBundle;
+import java.util.TreeSet;
+
+import javax.swing.JComponent;
+import javax.swing.JMenuItem;
+import javax.swing.JPopupMenu;
+import javax.swing.JTable;
+import javax.swing.ListSelectionModel;
+import javax.swing.RowSorter;
+import javax.swing.SortOrder;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.table.TableRowSorter;
+
+import net.oni2.aeinstaller.backend.mods.Mod;
+import net.oni2.aeinstaller.backend.mods.Type;
+
+/**
+ * @author Christian Illy
+ */
+public class ModTable extends JTable {
+	private static final long serialVersionUID = 1L;
+
+	private ResourceBundle bundle = ResourceBundle
+			.getBundle("net.oni2.aeinstaller.localization.ModTable");
+
+	private HashSet<ModSelectionListener> modSelListeners = new HashSet<ModSelectionListener>();
+
+	private ModTableModel model;
+	private TableRowSorter<ModTableModel> sorter;
+
+	/**
+	 * Create a new ModTable
+	 */
+	public ModTable() {
+		super();
+
+		setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+		getSelectionModel().addListSelectionListener(
+				new ListSelectionListener() {
+					@Override
+					public void valueChanged(ListSelectionEvent e) {
+						int viewRow = getSelectedRow();
+						if (viewRow < 0) {
+							notifyModSelectionListeners(null);
+						} else {
+							Mod mod = (Mod) getValueAt(viewRow, -1);
+							notifyModSelectionListeners(mod);
+						}
+					}
+				});
+		addMouseListener(new MouseAdapter() {
+			private void common(MouseEvent e) {
+				int r = rowAtPoint(e.getPoint());
+				if (r >= 0 && r < getRowCount())
+					setRowSelectionInterval(r, r);
+				else
+					clearSelection();
+
+				int rowindex = getSelectedRow();
+				if (rowindex >= 0) {
+					if (e.isPopupTrigger()
+							&& e.getComponent() instanceof JTable) {
+						final Mod mod = (Mod) getValueAt(rowindex, -1);
+
+						if (mod.isLocalAvailable()) {
+							JPopupMenu popup = new JPopupMenu();
+							JMenuItem openModFolder = new JMenuItem(
+									bundle.getString("openModFolder.text"));
+							openModFolder
+									.addActionListener(new ActionListener() {
+										@Override
+										public void actionPerformed(
+												ActionEvent arg0) {
+											try {
+												Desktop.getDesktop().open(
+														mod.getLocalPath());
+											} catch (IOException e) {
+												e.printStackTrace();
+											}
+										}
+									});
+							popup.add(openModFolder);
+							popup.show(e.getComponent(), e.getX(), e.getY());
+						}
+					}
+				}
+			}
+
+			@Override
+			public void mousePressed(MouseEvent e) {
+				common(e);
+			}
+
+			@Override
+			public void mouseReleased(MouseEvent e) {
+				common(e);
+			}
+		});
+		// To get checkbox-cells with background of row
+		((JComponent) getDefaultRenderer(Boolean.class)).setOpaque(true);
+
+		model = new ModTableModel();
+
+		setModel(model);
+
+		sorter = new TableRowSorter<ModTableModel>(model);
+		setRowSorter(sorter);
+
+		setFilter(null, 0);
+
+		List<RowSorter.SortKey> sortKeys = new ArrayList<RowSorter.SortKey>();
+		sortKeys.add(new RowSorter.SortKey(1, SortOrder.ASCENDING));
+		sorter.setSortKeys(sortKeys);
+
+		for (int i = 0; i < model.getColumnCount(); i++) {
+			model.setColumnConstraints(i, getColumnModel().getColumn(i));
+		}
+	}
+
+	@Override
+	public String getToolTipText(MouseEvent e) {
+		int r = rowAtPoint(e.getPoint());
+		int c = columnAtPoint(e.getPoint());
+		if (r >= 0 && r < getRowCount()) {
+			int modelCol = convertColumnIndexToModel(c);
+			if (modelCol == 4) {
+				final Mod mod = (Mod) getValueAt(r, -1);
+
+				String tt = "<html>";
+				tt += String.format("%s: %s<br>",
+						bundle.getString("state.installed"),
+						bundle.getString((mod.isInstalled() ? "yes" : "no")));
+				tt += String.format(
+						"%s: %s<br>",
+						bundle.getString("state.updatable"),
+						bundle.getString((mod.isLocalAvailable()
+								&& mod.isNewerAvailable() ? "yes" : "no")));
+				tt += String.format("%s: %s</html>", bundle
+						.getString("state.downloaded"), bundle.getString((mod
+						.isLocalAvailable() ? "yes" : "no")));
+				return tt;
+			}
+		}
+		return super.getToolTipText(e);
+	}
+
+	/**
+	 * @param listener
+	 *            Listener to add
+	 */
+	public void addModSelectionListener(ModSelectionListener listener) {
+		modSelListeners.add(listener);
+	}
+
+	/**
+	 * @param listener
+	 *            Listener to remove
+	 */
+	public void removeModSelectionListener(ModSelectionListener listener) {
+		modSelListeners.remove(listener);
+	}
+
+	private void notifyModSelectionListeners(Mod m) {
+		for (ModSelectionListener l : modSelListeners) {
+			l.modSelectionChanged(this, m);
+		}
+	}
+
+	/**
+	 * @param listener
+	 *            Listener to add
+	 */
+	public void addDownloadSizeListener(DownloadSizeListener listener) {
+		model.addDownloadSizeListener(listener);
+	}
+
+	/**
+	 * @param listener
+	 *            Listener to remove
+	 */
+	public void removeDownloadSizeListener(DownloadSizeListener listener) {
+		model.removeDownloadSizeListener(listener);
+	}
+
+	/**
+	 * Reload the nodes data after an update to the cache
+	 */
+	public void reloadData() {
+		model.reloadData();
+	}
+
+	/**
+	 * Revert the selection to the mods that are currently installed
+	 */
+	public void revertSelection() {
+		model.revertSelection();
+	}
+
+	/**
+	 * Reload the selection after a config was loaded
+	 * 
+	 * @param config
+	 *            Config to load
+	 */
+	public void reloadSelection(File config) {
+		model.reloadSelection(config);
+	}
+
+	/**
+	 * @return Mods selected for installation
+	 */
+	public TreeSet<Mod> getSelectedMods() {
+		return model.getSelectedMods();
+	}
+
+	/**
+	 * @param type
+	 *            Type of mods to show (null for all)
+	 * @param downloadState
+	 *            Show only: 0 = all, 1 = online, 2 = downloaded
+	 */
+	public void setFilter(Type type, int downloadState) {
+		sorter.setRowFilter(new ModTableFilter(type, downloadState));
+	}
+}
Index: AE/installer2/src/net/oni2/aeinstaller/gui/modtable/ModTableModel.java
===================================================================
--- AE/installer2/src/net/oni2/aeinstaller/gui/modtable/ModTableModel.java	(revision 630)
+++ AE/installer2/src/net/oni2/aeinstaller/gui/modtable/ModTableModel.java	(revision 631)
@@ -21,6 +21,5 @@
 
 	private ResourceBundle bundle = ResourceBundle
-			.getBundle("net.oni2.aeinstaller.localization."
-					+ getClass().getSimpleName());
+			.getBundle("net.oni2.aeinstaller.localization.ModTable");
 
 	private Vector<Mod> items = new Vector<Mod>();
@@ -33,5 +32,4 @@
 	 */
 	public ModTableModel() {
-		reloadData();
 	}
 
@@ -50,4 +48,11 @@
 			case 3:
 				return mod.getCreator();
+			case 4:
+				String res = "";
+				res += (install.get(row) ? "I" : "_");
+				res += (mod.isLocalAvailable() && mod.isNewerAvailable() ? "U"
+						: "_");
+				res += (mod.isLocalAvailable() ? "D" : "_");
+				return res;
 		}
 		return null;
@@ -65,4 +70,6 @@
 			case 3:
 				return bundle.getString("mod.creator");
+			case 4:
+				return bundle.getString("mod.state");
 		}
 		return null;
@@ -76,5 +83,5 @@
 	@Override
 	public int getColumnCount() {
-		return 4;
+		return 5;
 	}
 
@@ -89,4 +96,6 @@
 				return String.class;
 			case 3:
+				return String.class;
+			case 4:
 				return String.class;
 		}
@@ -123,4 +132,10 @@
 				col.setPreferredWidth(90);
 				break;
+			case 4:
+				w = 55;
+				col.setPreferredWidth(w);
+				col.setMinWidth(w);
+				col.setMaxWidth(w);
+				break;
 		}
 	}
@@ -141,6 +156,5 @@
 		install.clear();
 		for (int i = 0; i < items.size(); i++) {
-			install.add(i, ModManager.getInstance()
-					.isModInstalled(items.get(i)));
+			install.add(i, items.get(i).isInstalled());
 		}
 		fireTableDataChanged();
