source: AE/installer2/src/net/oni2/aeinstaller/gui/modtable/ModTable.java@ 664

Last change on this file since 664 was 660, checked in by alloc, 12 years ago

AEI2 0.99k:

  • Added mod string filter
  • Made jump-to-mod by key-press cycle through mods if pressed multiple times
File size: 13.5 KB
Line 
1package net.oni2.aeinstaller.gui.modtable;
2
3import java.awt.Desktop;
4import java.awt.Rectangle;
5import java.awt.event.ActionEvent;
6import java.awt.event.ActionListener;
7import java.awt.event.KeyAdapter;
8import java.awt.event.KeyEvent;
9import java.awt.event.MouseAdapter;
10import java.awt.event.MouseEvent;
11import java.io.File;
12import java.io.IOException;
13import java.util.ArrayList;
14import java.util.HashSet;
15import java.util.List;
16import java.util.ResourceBundle;
17import java.util.TreeSet;
18import java.util.Vector;
19
20import javax.swing.JCheckBoxMenuItem;
21import javax.swing.JComponent;
22import javax.swing.JMenuItem;
23import javax.swing.JOptionPane;
24import javax.swing.JPopupMenu;
25import javax.swing.JTable;
26import javax.swing.JViewport;
27import javax.swing.ListSelectionModel;
28import javax.swing.RowSorter;
29import javax.swing.SortOrder;
30import javax.swing.event.ListSelectionEvent;
31import javax.swing.event.RowSorterEvent;
32import javax.swing.table.JTableHeader;
33import javax.swing.table.TableColumn;
34import javax.swing.table.TableColumnModel;
35import javax.swing.table.TableRowSorter;
36
37import net.oni2.aeinstaller.backend.Settings;
38import net.oni2.aeinstaller.backend.packages.Package;
39import net.oni2.aeinstaller.backend.packages.Type;
40
41/**
42 * @author Christian Illy
43 */
44public class ModTable extends JTable {
45 private static final long serialVersionUID = 1L;
46
47 private ResourceBundle bundle = ResourceBundle
48 .getBundle("net.oni2.aeinstaller.localization.ModTable");
49
50 /**
51 * @author Christian Illy
52 */
53 public enum ETableContentType {
54 /**
55 * Table showing mods
56 */
57 MODS,
58 /**
59 * Table showing tools
60 */
61 TOOLS,
62 /**
63 * Table showing core packages
64 */
65 CORE
66 };
67
68 private HashSet<ModSelectionListener> modSelListeners = new HashSet<ModSelectionListener>();
69
70 private ModTableModel model;
71 private TableRowSorter<ModTableModel> sorter;
72
73 private ETableContentType contentType = ETableContentType.MODS;
74
75 /**
76 * Create a new ModTable
77 *
78 * @param contentType
79 * Content to show
80 */
81 public ModTable(ETableContentType contentType) {
82 super();
83
84 this.contentType = contentType;
85
86 setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
87 getSelectionModel().addListSelectionListener(this);
88 addMouseListener(new MouseEventHandler());
89 addKeyListener(new KeyEventHandler());
90 // To get checkbox-cells with background of row
91 ((JComponent) getDefaultRenderer(Boolean.class)).setOpaque(true);
92
93 model = new ModTableModel(contentType);
94
95 setModel(model);
96
97 sorter = new TableRowSorter<ModTableModel>(model);
98 setRowSorter(sorter);
99
100 setFilter(null, 0, null, EApplyFilterTo.ALL);
101
102 List<RowSorter.SortKey> sortKeys = new ArrayList<RowSorter.SortKey>();
103
104 int sortCol = Settings.getInstance().get("modSortColumn", 1);
105 SortOrder sortOrder = SortOrder.valueOf(Settings.getInstance().get(
106 "modSortOrder", "ASCENDING"));
107
108 sortKeys.add(new RowSorter.SortKey(sortCol, sortOrder));
109 sorter.setSortKeys(sortKeys);
110
111 for (int i = 0; i < model.getColumnCount(); i++) {
112 model.setColumnConstraints(i, getColumnModel().getColumn(i));
113 }
114
115 getTableHeader().addMouseListener(new HeaderMouseEventHandler());
116
117 if (contentType != ETableContentType.MODS) {
118 getColumnModel().removeColumn(getColumnModel().getColumn(0));
119 }
120 }
121
122 @Override
123 public String getToolTipText(MouseEvent e) {
124 int r = rowAtPoint(e.getPoint());
125 int c = columnAtPoint(e.getPoint());
126 if (r >= 0 && r < getRowCount()) {
127 int modelCol = convertColumnIndexToModel(c);
128 if (modelCol == 4) {
129 final Package mod = (Package) getValueAt(r, -1);
130
131 String tt = "<html>";
132 tt += String.format("%s: %s<br>",
133 bundle.getString("state.installed"),
134 bundle.getString((mod.isInstalled() ? "yes" : "no")));
135 tt += String.format(
136 "%s: %s<br>",
137 bundle.getString("state.updatable"),
138 bundle.getString((mod.isLocalAvailable()
139 && mod.isNewerAvailable() ? "yes" : "no")));
140 tt += String.format("%s: %s</html>", bundle
141 .getString("state.downloaded"), bundle.getString((mod
142 .isLocalAvailable() ? "yes" : "no")));
143 return tt;
144 }
145 }
146 return super.getToolTipText(e);
147 }
148
149 /**
150 * @param listener
151 * Listener to add
152 */
153 public void addModSelectionListener(ModSelectionListener listener) {
154 modSelListeners.add(listener);
155 }
156
157 /**
158 * @param listener
159 * Listener to remove
160 */
161 public void removeModSelectionListener(ModSelectionListener listener) {
162 modSelListeners.remove(listener);
163 }
164
165 private void notifyModSelectionListeners(Package m) {
166 for (ModSelectionListener l : modSelListeners) {
167 l.modSelectionChanged(this, m);
168 }
169 }
170
171 /**
172 * @param listener
173 * Listener to add
174 */
175 public void addDownloadSizeListener(DownloadSizeListener listener) {
176 model.addDownloadSizeListener(listener);
177 }
178
179 /**
180 * @param listener
181 * Listener to remove
182 */
183 public void removeDownloadSizeListener(DownloadSizeListener listener) {
184 model.removeDownloadSizeListener(listener);
185 }
186
187 /**
188 * Reload the nodes data after an update to the cache
189 */
190 public void reloadData() {
191 model.reloadData();
192 }
193
194 /**
195 * Revert the selection to the mods that are currently installed
196 */
197 public void revertSelection() {
198 model.revertSelection();
199 }
200
201 /**
202 * Reload the selection after a config was loaded
203 *
204 * @param config
205 * Config to load
206 */
207 public void reloadSelection(File config) {
208 model.reloadSelection(config);
209 }
210
211 /**
212 * @return Mods selected for installation
213 */
214 public TreeSet<Package> getSelectedMods() {
215 return model.getSelectedMods();
216 }
217
218 /**
219 * @param type
220 * Type of mods to show (null for all)
221 * @param downloadState
222 * Show only: 0 = all, 1 = online, 2 = downloaded
223 * @param filterString
224 * String to filter on
225 * @param filterTo
226 * Fields to use string filter on
227 */
228 public void setFilter(Type type, int downloadState, String filterString,
229 EApplyFilterTo filterTo) {
230 sorter.setRowFilter(new ModTableFilter(type, downloadState,
231 filterString, filterTo, contentType == ETableContentType.CORE,
232 false));
233 }
234
235 @Override
236 public void sorterChanged(RowSorterEvent evt) {
237 super.sorterChanged(evt);
238 if (evt.getType() == RowSorterEvent.Type.SORT_ORDER_CHANGED) {
239 @SuppressWarnings("unchecked")
240 RowSorter<ModTableModel> rs = (RowSorter<ModTableModel>) getRowSorter();
241 List<? extends RowSorter.SortKey> keys = rs.getSortKeys();
242 if (keys.size() > 0) {
243 int col = keys.get(0).getColumn();
244 SortOrder so = keys.get(0).getSortOrder();
245 Settings.getInstance().put("modSortColumn", col);
246 Settings.getInstance().put("modSortOrder", so.toString());
247 }
248 }
249 }
250
251 /**
252 * Select/Unselect all currently visible items depending on the current
253 * state of selection
254 */
255 public void unSelectAll() {
256 boolean isAll = true;
257 for (int i = 0; i < getRowCount(); i++) {
258 int modRow = convertRowIndexToModel(i);
259 boolean inst = (Boolean) model.getValueAt(modRow, 0);
260 if (!inst) {
261 isAll = false;
262 break;
263 }
264 }
265
266 for (int i = 0; i < getRowCount(); i++) {
267 int modRow = convertRowIndexToModel(i);
268 model.setValueAt(!isAll, modRow, 0);
269 }
270 invalidate();
271 repaint();
272 }
273
274 @Override
275 public void valueChanged(ListSelectionEvent e) {
276 super.valueChanged(e);
277 int viewRow = getSelectedRow();
278 if (viewRow < 0) {
279 notifyModSelectionListeners(null);
280 } else {
281 Package mod = (Package) getValueAt(viewRow, -1);
282 notifyModSelectionListeners(mod);
283 }
284 }
285
286 private class MouseEventHandler extends MouseAdapter {
287 private void mouseEventProcessing(MouseEvent e) {
288 int r = rowAtPoint(e.getPoint());
289 if (r >= 0 && r < getRowCount())
290 setRowSelectionInterval(r, r);
291 else
292 clearSelection();
293
294 int rowindex = getSelectedRow();
295 if (rowindex >= 0) {
296 if (e.isPopupTrigger() && e.getComponent() instanceof JTable) {
297 final Package mod = (Package) getValueAt(rowindex, -1);
298
299 JPopupMenu popup = new JPopupMenu();
300
301 if (mod.isLocalAvailable()) {
302 // Open package folder item
303 JMenuItem openModFolder = new JMenuItem(
304 bundle.getString("openModFolder.text"));
305 openModFolder.addActionListener(new ActionListener() {
306 @Override
307 public void actionPerformed(ActionEvent arg0) {
308 try {
309 Desktop.getDesktop().open(
310 mod.getLocalPath());
311 } catch (IOException e) {
312 e.printStackTrace();
313 }
314 }
315 });
316 popup.add(openModFolder);
317 }
318
319 if (mod.getUrl() != null) {
320 // Open Depot page item
321 JMenuItem openDepotPage = new JMenuItem(
322 bundle.getString("openDepotPage.text"));
323 openDepotPage.addActionListener(new ActionListener() {
324 @Override
325 public void actionPerformed(ActionEvent arg0) {
326 try {
327 Desktop.getDesktop().browse(mod.getUrl());
328 } catch (IOException e) {
329 e.printStackTrace();
330 }
331 }
332 });
333 popup.add(openDepotPage);
334 }
335
336 if (mod.isLocalAvailable()
337 && contentType != ETableContentType.CORE) {
338 // Delete package folder item
339 JMenuItem deleteModFolder = new JMenuItem(
340 bundle.getString("deletePackage.text"));
341 deleteModFolder.addActionListener(new ActionListener() {
342 @Override
343 public void actionPerformed(ActionEvent arg0) {
344 if (mod.getNode() == null) {
345 JOptionPane.showMessageDialog(
346 null,
347 bundle.getString("deletePackageLocalOnly.text"),
348 bundle.getString("deletePackageLocalOnly.title"),
349 JOptionPane.INFORMATION_MESSAGE);
350 } else {
351 int res = JOptionPane.showConfirmDialog(
352 null,
353 bundle.getString("deletePackageConfirm.text"),
354 bundle.getString("deletePackageConfirm.title"),
355 JOptionPane.YES_NO_OPTION,
356 JOptionPane.WARNING_MESSAGE);
357 if (res == JOptionPane.YES_OPTION) {
358 mod.deleteLocalPackage();
359 invalidate();
360 repaint();
361 }
362 }
363 }
364 });
365 popup.add(deleteModFolder);
366 }
367
368 if (popup.getSubElements().length > 0)
369 popup.show(e.getComponent(), e.getX(), e.getY());
370 }
371 }
372 }
373
374 @Override
375 public void mousePressed(MouseEvent e) {
376 mouseEventProcessing(e);
377 }
378
379 @Override
380 public void mouseReleased(MouseEvent e) {
381 mouseEventProcessing(e);
382 }
383 }
384
385 private class KeyEventHandler extends KeyAdapter {
386 private void goToRow(int row) {
387 setRowSelectionInterval(row, row);
388 JViewport viewport = (JViewport) getParent();
389 Rectangle rect = getCellRect(row, 0, true);
390 Rectangle r2 = viewport.getVisibleRect();
391 scrollRectToVisible(new Rectangle(rect.x, rect.y,
392 (int) r2.getWidth(), (int) r2.getHeight()));
393 }
394
395 @Override
396 public void keyTyped(KeyEvent e) {
397 super.keyTyped(e);
398
399 if (e.getModifiers() == 0) {
400 String key = String.valueOf(e.getKeyChar()).toLowerCase();
401 int row = getSelectedRow();
402 if (row == (getRowCount() - 1))
403 row = -1;
404 for (int i = row + 1; i < getRowCount(); i++) {
405 Package m = (Package) getValueAt(i, -1);
406 if (m.getName().toLowerCase().startsWith(key)) {
407 goToRow(i);
408 return;
409 }
410 }
411 if (row > 0) {
412 for (int i = 0; i < row; i++) {
413 Package m = (Package) getValueAt(i, -1);
414 if (m.getName().toLowerCase().startsWith(key)) {
415 goToRow(i);
416 return;
417 }
418 }
419 }
420 }
421 }
422 }
423
424 private class HeaderMouseEventHandler extends MouseAdapter {
425 private Vector<TableColumn> columns = new Vector<TableColumn>();
426 private TableColumnModel tcm = getColumnModel();
427
428 public HeaderMouseEventHandler() {
429 super();
430
431 for (int i = 1; i < tcm.getColumnCount(); i++) {
432 columns.add(tcm.getColumn(i));
433 }
434
435 for (int i = 1; i < columns.size(); i++) {
436 TableColumn tc = columns.get(i);
437 if (!Settings.getInstance().get(
438 String.format("modShowColumn%02d", tc.getModelIndex()),
439 true))
440 tcm.removeColumn(tc);
441 }
442 }
443
444 private TableColumn getColumn(String name) {
445 for (TableColumn tc : columns) {
446 if (tc.getHeaderValue().equals(name)) {
447 return tc;
448 }
449 }
450 return null;
451 }
452
453 private int headerContains(TableColumn tc) {
454 for (int col = 0; col < tcm.getColumnCount(); col++) {
455 if (tcm.getColumn(col).equals(tc))
456 return col;
457 }
458 return -1;
459 }
460
461 private void mouseEventProcessing(MouseEvent e) {
462 if (e.isPopupTrigger() && e.getComponent() instanceof JTableHeader) {
463 JPopupMenu popup = new JPopupMenu();
464
465 ActionListener al = new ActionListener() {
466 @Override
467 public void actionPerformed(ActionEvent e) {
468 JCheckBoxMenuItem itm = (JCheckBoxMenuItem) e
469 .getSource();
470 TableColumn col = getColumn(itm.getText());
471 if (itm.isSelected()) {
472 tcm.addColumn(col);
473 for (int i = columns.indexOf(col) - 1; i >= 0; i--) {
474 int pos = headerContains(columns.get(i));
475 if (pos >= 0) {
476 tcm.moveColumn(tcm.getColumnCount() - 1,
477 pos + 1);
478 break;
479 }
480 }
481 } else {
482 tcm.removeColumn(col);
483 }
484 Settings.getInstance().put(
485 String.format("modShowColumn%02d",
486 col.getModelIndex()), itm.isSelected());
487 }
488 };
489
490 for (int i = 1; i < columns.size(); i++) {
491 JCheckBoxMenuItem itm = new JCheckBoxMenuItem(
492 (String) columns.get(i).getHeaderValue());
493 itm.setSelected(headerContains(columns.get(i)) >= 0);
494
495 itm.addActionListener(al);
496 popup.add(itm);
497 }
498
499 if (popup.getSubElements().length > 0)
500 popup.show(e.getComponent(), e.getX(), e.getY());
501 }
502 }
503
504 @Override
505 public void mousePressed(MouseEvent e) {
506 mouseEventProcessing(e);
507 }
508
509 @Override
510 public void mouseReleased(MouseEvent e) {
511 mouseEventProcessing(e);
512 }
513 }
514
515}
Note: See TracBrowser for help on using the repository browser.