diff --git a/.gitattributes b/.gitattributes index e32e4d5cf44..d40ec7bfe1e 100644 --- a/.gitattributes +++ b/.gitattributes @@ -14794,6 +14794,21 @@ src/main/java/forge/gui/MultiLineLabelUI.java svneol=native#text/plain src/main/java/forge/gui/SOverlayUtils.java -text src/main/java/forge/gui/UnsortedListModel.java -text src/main/java/forge/gui/WrapLayout.java -text +src/main/java/forge/gui/cardlistview/AlwaysShowToolTip.java -text +src/main/java/forge/gui/cardlistview/EditorTableModel.java -text +src/main/java/forge/gui/cardlistview/EditorTableView.java -text +src/main/java/forge/gui/cardlistview/ITableContainer.java -text +src/main/java/forge/gui/cardlistview/IntegerRenderer.java -text +src/main/java/forge/gui/cardlistview/ManaCostRenderer.java -text +src/main/java/forge/gui/cardlistview/SCardListViewIO.java -text +src/main/java/forge/gui/cardlistview/SCardListViewUtil.java -text +src/main/java/forge/gui/cardlistview/SColumnUtil.java -text +src/main/java/forge/gui/cardlistview/SFilterUtil.java -text +src/main/java/forge/gui/cardlistview/SetCodeRenderer.java -text +src/main/java/forge/gui/cardlistview/TableColumnInfo.java -text +src/main/java/forge/gui/cardlistview/TableSorter.java -text +src/main/java/forge/gui/cardlistview/TableSorterCascade.java -text +src/main/java/forge/gui/cardlistview/package-info.java -text src/main/java/forge/gui/deckeditor/CDeckEditorUI.java -text src/main/java/forge/gui/deckeditor/DeckImport.java -text src/main/java/forge/gui/deckeditor/SEditorIO.java -text diff --git a/src/main/java/forge/gui/cardlistview/AlwaysShowToolTip.java b/src/main/java/forge/gui/cardlistview/AlwaysShowToolTip.java new file mode 100644 index 00000000000..2d941460cd3 --- /dev/null +++ b/src/main/java/forge/gui/cardlistview/AlwaysShowToolTip.java @@ -0,0 +1,25 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Forge Team + * + * 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 3 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, see . + */ +package forge.gui.cardlistview; + +/** + * A marker interface for indicating that tooltips should always be shown for + * cells rendered with the marked renderer. + */ +interface AlwaysShowToolTip { +} diff --git a/src/main/java/forge/gui/cardlistview/EditorTableModel.java b/src/main/java/forge/gui/cardlistview/EditorTableModel.java new file mode 100644 index 00000000000..b0e6d5b77ac --- /dev/null +++ b/src/main/java/forge/gui/cardlistview/EditorTableModel.java @@ -0,0 +1,401 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 +import forge.gui.deckeditor.views.VDeckEditorUI; +Forge Team + * + * 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 3 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, see . + */ +package forge.gui.cardlistview; + +import java.awt.Cursor; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Enumeration; +import java.util.List; +import java.util.Map.Entry; + +import javax.swing.JTable; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import javax.swing.event.TableModelEvent; +import javax.swing.table.AbstractTableModel; +import javax.swing.table.JTableHeader; +import javax.swing.table.TableColumn; +import javax.swing.table.TableColumnModel; + +import org.apache.commons.lang3.ArrayUtils; + +import forge.gui.cardlistview.SColumnUtil.ColumnName; +import forge.gui.cardlistview.SColumnUtil.SortState; +import forge.gui.deckeditor.CDeckEditorUI; +import forge.item.InventoryItem; +import forge.item.ItemPool; +import forge.item.ItemPoolView; + +/** + *

+ * EditorTableModel class. + *

+ * + * @param + * the generic type + * @author Forge + * @version $Id: EditorTableModel.java 19857 2013-02-24 08:49:52Z Max mtg $ + */ +@SuppressWarnings("serial") +public final class EditorTableModel extends AbstractTableModel { + private final ItemPool data; + private final JTable table; + private final CascadeManager cascadeManager = new CascadeManager(); + private final int maxSortDepth = 3; + private boolean infiniteSupply; + + /** + * Instantiates a new table model, using a JTable, + * a column set, and a data set of generic type . + * + * @param table0   {@link javax.swing.JTable} + * @param class0   Generic type + */ + public EditorTableModel(final JTable table0, final Class class0) { + this.table = table0; + this.data = new ItemPool(class0); + } + + /** */ + @SuppressWarnings("unchecked") + public void setup() { + final Enumeration e = table.getColumnModel().getColumns(); + final TableColumn[] sortcols = new TableColumn[table.getColumnCount()]; + + // Assemble priority sort. + while (e.hasMoreElements()) { + final TableColumnInfo col = (TableColumnInfo) e.nextElement(); + if (col.getSortPriority() > 0) { + sortcols[col.getSortPriority()] = col; + } + } + + final boolean isDeckTable = ((TableColumnInfo) table.getColumnModel() + .getColumn(0)).getEnumValue().substring(0, 4).equals("DECK") + ? true : false; + + if (sortcols[1] == null) { + if (isDeckTable) { + cascadeManager.add((TableColumnInfo) SColumnUtil.getColumn(ColumnName.DECK_NAME)); + } + else { + cascadeManager.add((TableColumnInfo) SColumnUtil.getColumn(ColumnName.CAT_NAME)); + } + } + else { + ArrayUtils.reverse(sortcols); + for (int i = 1; i < sortcols.length; i++) { + if (sortcols[i] != null) { + cascadeManager.add((TableColumnInfo) sortcols[i]); + } + } + } + } + + /** + * Clears all data in the model. + */ + public void clear() { + this.data.clear(); + } + + /** + * Gets all cards in the model. + * + * @return the cards + */ + public ItemPoolView getCards() { + return this.data.getView(); + } + + /** + * Removes a card from the model. + * + * @param card0   {@link forge.Card} object + */ + public void removeCard(final T card0, int qty) { + if ( isInfinite() ) + return; + + final boolean wasThere = this.data.count(card0) > 0; + if (wasThere) { + this.data.remove(card0, qty); + this.fireTableDataChanged(); + } + } + + /** + * Adds a card to the model. + * + * @param card0   {@link forge.Card} object. + */ + public void addCard(final T card0, int qty) { + this.data.add(card0, qty); + this.fireTableDataChanged(); + } + + /** + * Adds multiple copies of multiple cards to the model. + * + * @param cards0   {@link java.lang.Iterable}> + */ + public void addCards(final Iterable> cards0) { + this.data.addAll(cards0); + this.fireTableDataChanged(); + } + + /** + * Row to card. + * + * @param row + * the row + * @return the entry + */ + public Entry rowToCard(final int row) { + final List> model = this.data.getOrderedList(); + return (row >= 0) && (row < model.size()) ? model.get(row) : null; + } + + /** + * Show selected card. + * + * @param table + * the table + */ + public void showSelectedCard(final JTable table) { + final int row = table.getSelectedRow(); + if (row != -1) { + Entry card = this.rowToCard(row); + CDeckEditorUI.SINGLETON_INSTANCE.setCard(null != card ? card.getKey() : null); + } + } + + /** + *

+ * addListeners. + *

+ */ + public void addListeners() { + // updates card detail, listens to any key strokes + table.getSelectionModel().addListSelectionListener(new ListSelectionListener() { + @Override + public void valueChanged(final ListSelectionEvent arg0) { + if (table.isFocusOwner()) { + EditorTableModel.this.showSelectedCard(table); + } + } + }); + + table.addFocusListener(new FocusAdapter() { + @Override + public void focusGained(final FocusEvent e) { + EditorTableModel.this.showSelectedCard(table); + } + }); + + final JTableHeader header = table.getTableHeader(); + header.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(final MouseEvent e) { + if (Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR) != header.getCursor()) { + headerClicked(e); + } + } + + @Override + public void mouseReleased(MouseEvent e) { + SCardListViewIO.savePreferences(); + } + }); + } // addCardListener() + + /** + * Resort. + */ + public void refreshSort() { + if (this.data.getOrderedList().size() == 0) { return; } + + Collections.sort(this.data.getOrderedList(), new MyComparator()); + } + + @SuppressWarnings("unchecked") + private void headerClicked(final MouseEvent e) { + final TableColumnModel colModel = EditorTableModel.this.table.getColumnModel(); + final int columnModelIndex = colModel.getColumnIndexAtX(e.getX()); + final int modelIndex = colModel.getColumn(columnModelIndex).getModelIndex(); + + if (modelIndex < 0) { + return; + } + + // This will invert if needed + // 2012/07/21 - Changed from modelIndex to ColumnModelIndex due to a crash + // Crash was: Hide 2 columns, then search by last column. + EditorTableModel.this.cascadeManager.add((TableColumnInfo) this.table.getColumnModel().getColumn(columnModelIndex)); + EditorTableModel.this.refreshSort(); + EditorTableModel.this.table.tableChanged(new TableModelEvent(EditorTableModel.this)); + EditorTableModel.this.table.repaint(); + if (EditorTableModel.this.table.getRowCount() > 0) { + EditorTableModel.this.table.setRowSelectionInterval(0, 0); + } + SCardListViewIO.savePreferences(); + } + + //========== Overridden from AbstractTableModel + /** {@inheritDoc} */ + @Override + public int findColumn(final String name0) { + return table.getColumnModel().getColumnIndex(name0); + } + + /* (non-Javadoc) + * @see javax.swing.table.TableModel#getColumnCount() + */ + @Override + public int getColumnCount() { + return table.getColumnCount(); + } + + /* (non-Javadoc) + * @see javax.swing.table.TableModel#getRowCount() + */ + @Override + public int getRowCount() { + return this.data.countDistinct(); + } + + /* (non-Javadoc) + * @see javax.swing.table.TableModel#getValueAt(int, int) + */ + @Override + @SuppressWarnings("unchecked") + public Object getValueAt(int iRow, int iCol) { + Entry card = this.rowToCard(iRow); + if (null == card) { + return null; + } + return ((TableColumnInfo) table.getColumnModel().getColumn(table.convertColumnIndexToView(iCol))).getFnDisplay().apply(card); + } + + //========= Custom class handling + + /** + * Manages sorting orders for multiple depths of sorting. + */ + private final class CascadeManager { + private final List> colsToSort = new ArrayList>(3); + private TableSorterCascade sorter = null; + + // Adds a column to sort cascade list. + // If column is first in the cascade, inverts direction of sort. + // Otherwise, sorts in ascending direction. + @SuppressWarnings("unchecked") + public void add(final TableColumnInfo col0) { + this.sorter = null; + + // Found at top level, should invert + if (colsToSort.size() > 0 && colsToSort.get(0).equals(col0)) { + this.colsToSort.get(0).setSortState( + this.colsToSort.get(0).getSortState() == SortState.ASC + ? SortState.DESC : SortState.ASC); + this.colsToSort.get(0).setSortPriority(1); + } + // Found somewhere: move down others, this one to top. + else if (colsToSort.contains(col0)) { + col0.setSortState(SortState.ASC); + this.colsToSort.remove(col0); + this.colsToSort.add(0, (TableColumnInfo) col0); + } + // No column in list; add directly. + else { + col0.setSortState(SortState.ASC); + this.colsToSort.add(0, (TableColumnInfo) col0); + this.colsToSort.get(0).setSortPriority(1); + } + + // Decrement sort priority on remaining columns + for (int i = 1; i < maxSortDepth; i++) { + if (colsToSort.size() == i) { break; } + + if (colsToSort.get(i).getSortPriority() != 0) { + colsToSort.get(i).setSortPriority(i + 1); + } + } + + // Unset and remove boundary columns. + if (this.colsToSort.size() > maxSortDepth) { + this.colsToSort.get(maxSortDepth).setSortState(SortState.NONE); + this.colsToSort.get(maxSortDepth).setSortPriority(0); + this.colsToSort.remove(maxSortDepth); + } + } + + public TableSorterCascade getSorter() { + if (this.sorter == null) { + this.sorter = createSorter(); + } + return this.sorter; + } + + private TableSorterCascade createSorter() { + + final List> oneColSorters + = new ArrayList>(maxSortDepth); + + for (final TableColumnInfo col : this.colsToSort) { + oneColSorters.add(new TableSorter( + col.getFnSort(), + col.getSortState().equals(SortState.ASC) ? true : false)); + } + + return new TableSorterCascade(oneColSorters); + } + } + + private class MyComparator implements Comparator> { + /* (non-Javadoc) + * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) + */ + @SuppressWarnings("unchecked") + @Override + public int compare(Entry o1, Entry o2) { + return EditorTableModel.this.cascadeManager.getSorter().compare( + (Entry) o1, (Entry) o2); + } + } + + /** + * Sets whether this table's pool of cards is in infinite supply. If false, cards in the + * table have a limited number of copies. + */ + public void setInfinite(boolean infinite) { + this.infiniteSupply = infinite; + } + + public boolean isInfinite() { + return infiniteSupply; + } +} // CardTableModel diff --git a/src/main/java/forge/gui/cardlistview/EditorTableView.java b/src/main/java/forge/gui/cardlistview/EditorTableView.java new file mode 100644 index 00000000000..c19584a5306 --- /dev/null +++ b/src/main/java/forge/gui/cardlistview/EditorTableView.java @@ -0,0 +1,576 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Forge Team + * + * 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 3 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, see . + */ +package forge.gui.cardlistview; + +import java.awt.Color; +import java.awt.Component; +import java.awt.KeyboardFocusManager; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.awt.event.MouseEvent; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import javax.swing.JComponent; +import javax.swing.JTable; +import javax.swing.JViewport; +import javax.swing.event.TableModelEvent; +import javax.swing.event.TableModelListener; +import javax.swing.table.DefaultTableColumnModel; +import javax.swing.table.JTableHeader; +import javax.swing.table.TableCellRenderer; +import javax.swing.table.TableColumn; + +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.Iterables; + +import forge.gui.deckeditor.SEditorUtil; +import forge.gui.deckeditor.views.ITableContainer; +import forge.gui.toolbox.FSkin; +import forge.item.InventoryItem; +import forge.item.ItemPool; +import forge.item.ItemPoolView; +import forge.util.Aggregates; + + +/** + * TableWithCards. + * + * @param + * the generic type + */ +public final class EditorTableView { + private ItemPool pool; + private EditorTableModel model; + private final JTable table; + private Predicate filter = null; + private boolean wantUnique = false; + private boolean alwaysNonUnique = false; + + private final Class genericType; + + /** + * + * getTable. + * + * @return JTable + */ + public JTable getTable() { + return this.table; + } + + /** + * TableWithCards. + * + * @param type0 the class of item that this table will contain + */ + public EditorTableView(final Class type0) { + this(false, type0); + } + + /** + * TableWithCards Constructor. + * + * @param forceUnique whether this table should display only one item with the same name + * @param type0 the class of item that this table will contain + */ + @SuppressWarnings("serial") + public EditorTableView(final boolean forceUnique, final Class type0) { + this.genericType = type0; + this.wantUnique = forceUnique; + + // subclass JTable to show tooltips when hovering over column headers + // and cell data that are truncated due to too-small column widths + table = new JTable() { + private String _getCellTooltip( + TableCellRenderer renderer, int row, int col, Object val) { + Component cell = renderer.getTableCellRendererComponent( + table, val, false, false, row, col); + + // if we're conditionally showing the tooltip, check to see + // if we shouldn't show it + if (!(cell instanceof AlwaysShowToolTip)) + { + // if there's enough room (or there's no value), no tooltip + // we use '>' here instead of '>=' since that seems to be the + // threshold for where the ellipses appear for the default + // JTable renderer + int requiredWidth = cell.getPreferredSize().width; + TableColumn tableColumn = columnModel.getColumn(col); + if (null == val || tableColumn.getWidth() > requiredWidth) { + return null; + } + } + + // use a pre-set tooltip if it exists + if (cell instanceof JComponent) + { + JComponent jcell = (JComponent)cell; + String tip = jcell.getToolTipText(); + if (null != tip) + { + return tip; + } + } + + // otherwise, show the full text in the tooltip + return String.valueOf(val); + } + + // column headers + @Override + protected JTableHeader createDefaultTableHeader() { + return new JTableHeader(columnModel) { + public String getToolTipText(MouseEvent e) { + int col = columnModel.getColumnIndexAtX(e.getPoint().x); + TableColumn tableColumn = columnModel.getColumn(col); + TableCellRenderer headerRenderer = tableColumn.getHeaderRenderer(); + if (null == headerRenderer) { + headerRenderer = getDefaultRenderer(); + } + + return _getCellTooltip( + headerRenderer, -1, col, tableColumn.getHeaderValue()); + } + }; + } + + // cell data + @Override + public String getToolTipText(MouseEvent e) { + Point p = e.getPoint(); + int row = rowAtPoint(p); + int col = columnAtPoint(p); + + if (col >= table.getColumnCount() || row >= table.getRowCount()) { + return null; + } + + Object val = table.getValueAt(row, col); + if (null == val) { + return null; + } + + return _getCellTooltip(getCellRenderer(row, col), row, col, val); + } + + private int lastTooltipRow = -1; + private int lastTooltipCol = -1; + private Point lastTooltipPt; + + @Override + public Point getToolTipLocation(MouseEvent e) { + Point p = e.getPoint(); + final int row = rowAtPoint(p); + final int col = columnAtPoint(p); + if (row == lastTooltipRow && col == lastTooltipCol) { + p = lastTooltipPt; + } else { + lastTooltipRow = row; + lastTooltipCol = col; + lastTooltipPt = p; + } + return new Point(p.x + 10, p.y + 20); + } + }; + + // use different selection highlight colors for focused vs. unfocused tables + table.setSelectionBackground(FSkin.getColor(FSkin.Colors.CLR_INACTIVE)); + table.setSelectionForeground(FSkin.getColor(FSkin.Colors.CLR_TEXT)); + table.addFocusListener(new FocusListener() { + @Override + public void focusLost(FocusEvent e) { + if (!e.isTemporary()) { + table.setSelectionBackground(FSkin.getColor(FSkin.Colors.CLR_INACTIVE)); + } + } + + @Override + public void focusGained(FocusEvent e) { + table.setSelectionBackground(FSkin.getColor(FSkin.Colors.CLR_ACTIVE)); + // if nothing selected when we gain focus, select the first row (if exists) + if (-1 == table.getSelectedRow() && 0 < table.getRowCount()) { + table.setRowSelectionInterval(0, 0); + } + } + }); + + table.setFont(FSkin.getFont(12)); + table.setBorder(null); + table.getTableHeader().setBorder(null); + table.setRowHeight(18); + table.setAutoResizeMode(JTable.AUTO_RESIZE_NEXT_COLUMN); + + // prevent tables from intercepting tab focus traversals + table.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, null); + table.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, null); + } + + /** + * Applies a EditorTableModel and a model listener to this instance's JTable. + * + * @param view0   the {@link javax.gui.deckeditor.views.ITableCOntainer} + * @param cols0   List> of additional columns for this + */ + public void setup(final ITableContainer view0, final List> cols0) { + final DefaultTableColumnModel colmodel = new DefaultTableColumnModel(); + + for (TableColumnInfo item : cols0) { + item.setModelIndex(colmodel.getColumnCount()); + if (item.isShowing()) { colmodel.addColumn(item); } + } + + this.model = new EditorTableModel(this.table, this.genericType); + this.model.addListeners(); + this.table.setModel(this.model); + this.table.setColumnModel(colmodel); + + this.model.setup(); + this.model.refreshSort(); + + this.table.getTableHeader().setBackground(new Color(200, 200, 200)); + + // Update stats each time table changes + this.model.addTableModelListener(new TableModelListener() { + @Override + public void tableChanged(final TableModelEvent ev) { + SEditorUtil.setStats(EditorTableView.this.model.getCards(), view0); + } + }); + } + + public void setAvailableColumns(final List> cols0) { + final DefaultTableColumnModel colmodel = new DefaultTableColumnModel(); + + for (TableColumnInfo item : cols0) { + item.setModelIndex(colmodel.getColumnCount()); + if (item.isShowing()) { colmodel.addColumn(item); } + } + + this.table.setColumnModel(colmodel); + } + + /** + * + * fixSelection. Call this after deleting an item from table. + * + * @param rowLastSelected + * an int + */ + public void fixSelection(final int rowLastSelected) { + if (0 > rowLastSelected) { + return; + } + + // 3 cases: 0 cards left, select the same row, select prev row + int numRows = model.getRowCount(); + if (numRows == 0) { + return; + } + + int newRow = rowLastSelected; + if (numRows <= newRow) { + // move selection away from the last, already missing, option + newRow = numRows - 1; + } + + selectAndScrollTo(newRow); + } + + /** + * + * setDeck. + * + * @param cards + * an Iterable + */ + public void setDeck(final Iterable cards) { + this.setDeckImpl(ItemPool.createFrom(cards, this.genericType), false); + } + + /** + * setDeck. + * + * @param poolView + * an ItemPoolView + */ + public void setDeck(final ItemPoolView poolView, boolean infinite) { + this.setDeckImpl(ItemPool.createFrom(poolView, this.genericType), infinite); + + } + public void setDeck(final ItemPoolView poolView) { + this.setDeck(poolView, false); + } + /** + * Sets the deck. + * + * @param pool + * the new deck + */ + public void setDeck(final ItemPool pool) { + this.setDeckImpl(pool, false); + } + + /** + * + * setDeckImpl. + * + * @param thePool + * an ItemPool + */ + protected void setDeckImpl(final ItemPool thePool, boolean infinite) { + this.model.clear(); + this.pool = thePool; + this.model.addCards(this.pool); + this.model.setInfinite(infinite); + this.updateView(true); + } + + /** + * + * getSelectedCard. + * + * @return InventoryItem + */ + public InventoryItem getSelectedCard() { + final int iRow = this.table.getSelectedRow(); + return iRow >= 0 ? this.model.rowToCard(iRow).getKey() : null; + } + + /** + * returns all selected cards + */ + public List getSelectedCards() { + List items = new ArrayList(); + for (int row : table.getSelectedRows()) { + items.add(model.rowToCard(row).getKey()); + } + return items; + } + + private boolean isUnfiltered() { + return this.filter == null; + } + + /** + * + * setFilter. + * + * @param filterToSet + * a Predicate + */ + public void setFilter(final Predicate filterToSet) { + this.filter = filterToSet; + if (null != pool) { + this.updateView(true); + } + } + + /** + * + * addCard. + * + * @param card + * an InventoryItem + */ + public void addCard(final T card, int qty) { + final int n = this.table.getSelectedRow(); + this.pool.add(card, qty); + if (this.isUnfiltered()) { + this.model.addCard(card, qty); + } + this.updateView(false); + this.fixSelection(n); + } + + public void addCards(Iterable> cardsToAdd) { + final int n = this.table.getSelectedRow(); + for (Map.Entry item : cardsToAdd) { + this.pool.add(item.getKey(), item.getValue()); + if (this.isUnfiltered()) { + this.model.addCard(item.getKey(), item.getValue()); + } + } + this.updateView(false); + this.fixSelection(n); + } + + public void addCards(Collection cardsToAdd) { + final int n = this.table.getSelectedRow(); + for (T item : cardsToAdd) { + this.pool.add(item, 1); + if (this.isUnfiltered()) { + this.model.addCard(item, 1); + } + } + this.updateView(false); + this.fixSelection(n); + } + + /** + * + * removeCard. + * + * @param card + * an InventoryItem + */ + public void removeCard(final T card, int qty) { + final int n = this.table.getSelectedRow(); + this.pool.remove(card, qty); + if (this.isUnfiltered()) { + this.model.removeCard(card, qty); + } + this.updateView(false); + this.fixSelection(n); + } + + public void removeCards(List> cardsToRemove) { + final int n = this.table.getSelectedRow(); + for (Map.Entry item : cardsToRemove) { + this.pool.remove(item.getKey(), item.getValue()); + if (this.isUnfiltered()) { + this.model.removeCard(item.getKey(), item.getValue()); + } + } + this.updateView(false); + this.fixSelection(n); + } + + public int getCardCount(final T card) { + return model.isInfinite() ? Integer.MAX_VALUE : this.pool.count(card); + } + + public Predicate getFilter() { + return filter; + } + + /** + * + * updateView. + * + * @param bForceFilter + * a boolean + */ + public void updateView(final boolean bForceFilter) { + final boolean useFilter = (bForceFilter && (this.filter != null)) || !isUnfiltered(); + + if (useFilter || this.wantUnique || bForceFilter) { + this.model.clear(); + } + + if (useFilter && this.wantUnique) { + Predicate> filterForPool = Predicates.compose(this.filter, this.pool.FN_GET_KEY); + Iterable> cards = Aggregates.uniqueByLast(Iterables.filter(this.pool, filterForPool), this.pool.FN_GET_NAME); + this.model.addCards(cards); + } else if (useFilter) { + Predicate> pred = Predicates.compose(this.filter, this.pool.FN_GET_KEY); + this.model.addCards(Iterables.filter(this.pool, pred)); + } else if (this.wantUnique) { + Iterable> cards = Aggregates.uniqueByLast(this.pool, this.pool.FN_GET_NAME); + this.model.addCards(cards); + } else if (!useFilter && bForceFilter) { + this.model.addCards(this.pool); + } + + this.model.refreshSort(); + } + + /** + * + * getCards. + * + * @return ItemPoolView + */ + public ItemPoolView getCards() { + return this.pool; + } + + /** + * + * getWantUnique. + * + * @return true if the editor is in "unique card names only" mode. + */ + public boolean getWantUnique() { + return this.wantUnique; + } + + /** + * + * setWantUnique. + * + * @param unique if true, the editor will be set to the "unique card names only" mode. + */ + public void setWantUnique(boolean unique) { + this.wantUnique = this.alwaysNonUnique ? false : unique; + } + + /** + * + * getAlwaysNonUnique. + * + * @return if ture, this editor must always show non-unique cards (e.g. quest editor). + */ + public boolean getAlwaysNonUnique() { + return this.alwaysNonUnique; + } + + /** + * + * setAlwaysNonUnique. + * + * @param nonUniqueOnly if true, this editor must always show non-unique cards (e.g. quest editor). + */ + public void setAlwaysNonUnique(boolean nonUniqueOnly) { + this.alwaysNonUnique = nonUniqueOnly; + } + + public void setWantElasticColumns(boolean value) { + table.setAutoResizeMode(value ? JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS : JTable.AUTO_RESIZE_NEXT_COLUMN); + } + + public void selectAndScrollTo(int rowIdx) { + if (!(table.getParent() instanceof JViewport)) { + return; + } + JViewport viewport = (JViewport)table.getParent(); + + // compute where we're going and where we are + Rectangle targetRect = table.getCellRect(rowIdx, 0, true); + Rectangle curViewRect = viewport.getViewRect(); + + // if the target cell is not visible, attempt to jump to a location where it is + // visible but not on the edge of the viewport + if (targetRect.y + targetRect.height > curViewRect.y + curViewRect.height) { + // target is below us, move to position 3 rows below target + targetRect.setLocation(targetRect.x, targetRect.y + (targetRect.height * 3)); + } else if (targetRect.y < curViewRect.y) { + // target is above is, move to position 3 rows above target + targetRect.setLocation(targetRect.x, targetRect.y - (targetRect.height * 3)); + } + + table.scrollRectToVisible(targetRect); + table.setRowSelectionInterval(rowIdx, rowIdx); + } +} diff --git a/src/main/java/forge/gui/cardlistview/ITableContainer.java b/src/main/java/forge/gui/cardlistview/ITableContainer.java new file mode 100644 index 00000000000..9cbc42c6327 --- /dev/null +++ b/src/main/java/forge/gui/cardlistview/ITableContainer.java @@ -0,0 +1,25 @@ +package forge.gui.cardlistview; + +import javax.swing.JTable; + +import forge.gui.deckeditor.SEditorUtil; +import forge.gui.toolbox.FLabel; + +/** + * Dictates methods needed for a class to act as a container for + * a EditorTableView deck editing component. + * + *

(I at beginning of class name denotes an interface.) + * + */ +public interface ITableContainer { + /** + * Sets the table used for displaying cards in this + * deck editor container. + * + * @param tbl0   {@link forge.gui.deckeditor.tables.EditorTableView} + */ + void setTableView(JTable tbl0); + + FLabel getStatLabel(SCardListViewUtil.StatTypes s); + } diff --git a/src/main/java/forge/gui/cardlistview/IntegerRenderer.java b/src/main/java/forge/gui/cardlistview/IntegerRenderer.java new file mode 100644 index 00000000000..df2e72e0ff3 --- /dev/null +++ b/src/main/java/forge/gui/cardlistview/IntegerRenderer.java @@ -0,0 +1,44 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Forge Team + * + * 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 3 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, see . + */ +package forge.gui.cardlistview; + +import java.awt.Component; + +import javax.swing.JTable; +import javax.swing.table.DefaultTableCellRenderer; + +/** + * A quick converter to avoid -1 being displayed for unapplicable values. + */ +@SuppressWarnings("serial") +public class IntegerRenderer extends DefaultTableCellRenderer { + /* + * (non-Javadoc) + * + * @see + * javax.swing.table.DefaultTableCellRenderer#getTableCellRendererComponent + * (javax.swing.JTable, java.lang.Object, boolean, boolean, int, int) + */ + @Override + public final Component getTableCellRendererComponent(final JTable table, Object value0, + final boolean isSelected, final boolean hasFocus, final int row, final int column) { + + if ((Integer) value0 == -1) { value0 = "-"; } + return super.getTableCellRendererComponent(table, value0, isSelected, hasFocus, row, column); + } +} diff --git a/src/main/java/forge/gui/cardlistview/ManaCostRenderer.java b/src/main/java/forge/gui/cardlistview/ManaCostRenderer.java new file mode 100644 index 00000000000..80c7f81bf61 --- /dev/null +++ b/src/main/java/forge/gui/cardlistview/ManaCostRenderer.java @@ -0,0 +1,135 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Forge Team + * + * 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 3 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, see . + */ +package forge.gui.cardlistview; + +import java.awt.Component; +import java.awt.Graphics; +import java.util.List; + +import javax.swing.JTable; +import javax.swing.table.DefaultTableCellRenderer; + +import forge.card.CardRules; +import forge.card.CardSplitType; +import forge.card.mana.ManaCost; +import forge.card.mana.ManaCostShard; +import forge.gui.toolbox.CardFaceSymbols; + +/** + * Displays mana cost as symbols. + */ +public class ManaCostRenderer extends DefaultTableCellRenderer { + private static final long serialVersionUID = 1770527102334163549L; + + static final int elemtWidth = 13; + static final int elemtGap = 0; + static final int padding0 = 1; + static final int spaceBetweenSplitCosts = 3; + + private ManaCost v1; + private ManaCost v2; + + /* + * (non-Javadoc) + * + * @see + * javax.swing.table.DefaultTableCellRenderer#getTableCellRendererComponent + * (javax.swing.JTable, java.lang.Object, boolean, boolean, int, int) + */ + @Override + public final Component getTableCellRendererComponent(final JTable table, final Object value, + final boolean isSelected, final boolean hasFocus, final int row, final int column) { + CardRules v = value instanceof CardRules ? (CardRules) value : null; + this.v1 = v == null ? ManaCost.NO_COST : v.getMainPart().getManaCost(); + this.v2 = v == null || v.getSplitType() != CardSplitType.Split ? null : v.getOtherPart().getManaCost(); + this.setToolTipText(v2 == null ? v1.toString() : v1.toString() + " / " + v2.toString()); + return super.getTableCellRendererComponent(table, "", isSelected, hasFocus, row, column); + } + + /* + * (non-Javadoc) + * + * @see javax.swing.JComponent#paint(java.awt.Graphics) + */ + @Override + public final void paint(final Graphics g) { + super.paint(g); + + final int cellWidth = this.getWidth(); + + if ( null == v2 ) + drawCost(g, v1, padding0, cellWidth); + else + { + int shards1 = v1.isPureGeneric() || v1.getGenericCost() > 0 ? 1 : 0; + int shards2 = v2.isPureGeneric() || v2.getGenericCost() > 0 ? 1 : 0; + shards1 += v1.getShards().size(); + shards2 += v2.getShards().size(); + + int perGlyph = (cellWidth - padding0 - spaceBetweenSplitCosts) / (shards1 + shards2); + perGlyph = Math.min(perGlyph, elemtWidth + elemtGap); + drawCost(g, v1, padding0, padding0 + perGlyph * shards1); + drawCost(g, v2, cellWidth - perGlyph * shards2, cellWidth ); + } + } + + /** + * TODO: Write javadoc for this method. + * @param g + * @param padding + * @param cellWidth + */ + private void drawCost(final Graphics g, ManaCost value, final int padding, final int cellWidth) { + float xpos = padding; + final int genericManaCost = value.getGenericCost(); + final int xManaCosts = value.countX(); + final boolean hasGeneric = (genericManaCost > 0) || this.v1.isPureGeneric(); + final List shards = value.getShards(); + + + final int cntGlyphs = hasGeneric ? shards.size() + 1 : shards.size(); + final float offsetIfNoSpace = cntGlyphs > 1 ? (cellWidth - padding - elemtWidth) / (cntGlyphs - 1f) + : elemtWidth + elemtGap; + final float offset = Math.min(elemtWidth + elemtGap, offsetIfNoSpace); + + // Display X Mana before any other type of mana + if (xManaCosts > 0) { + for (int i = 0; i < xManaCosts; i++) { + CardFaceSymbols.drawSymbol(ManaCostShard.X.getImageKey(), g, (int) xpos, 1); + xpos += offset; + } + } + + // Display colorless mana before colored mana + if (hasGeneric) { + final String sGeneric = Integer.toString(genericManaCost); + CardFaceSymbols.drawSymbol(sGeneric, g, (int) xpos, 1); + xpos += offset; + } + + for (final ManaCostShard s : shards) { + if (s.equals(ManaCostShard.X)) { + // X costs already drawn up above + continue; + } + CardFaceSymbols.drawSymbol(s.getImageKey(), g, (int) xpos, 1); + xpos += offset; + } + } + +} diff --git a/src/main/java/forge/gui/cardlistview/SCardListViewIO.java b/src/main/java/forge/gui/cardlistview/SCardListViewIO.java new file mode 100644 index 00000000000..2c873288876 --- /dev/null +++ b/src/main/java/forge/gui/cardlistview/SCardListViewIO.java @@ -0,0 +1,239 @@ +package forge.gui.cardlistview; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.TreeMap; + +import javax.xml.stream.XMLEventFactory; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.XMLEventWriter; +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLOutputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.events.Attribute; +import javax.xml.stream.events.StartElement; +import javax.xml.stream.events.XMLEvent; + +import forge.gui.cardlistview.SColumnUtil.ColumnName; +import forge.item.InventoryItem; +import forge.properties.NewConstants; + +/** + * Handles editor preferences saving and loading. + * + *

(S at beginning of class name denotes a static factory.) + */ +public class SCardListViewIO { + /** Used in the XML IO to extract properties from PREFS file. */ + private enum ColumnProperty { /** */ + enumval, /** */ + identifier, /** */ + show, /** */ + sortpriority, /** */ + sortstate, /** */ + width + } + + /** Preferences (must match with PREFS file). */ + public enum EditorPreference { + stats_deck, + display_unique_only, + elastic_columns + } + + private static final XMLEventFactory EVENT_FACTORY = XMLEventFactory.newInstance(); + private static final XMLEvent NEWLINE = EVENT_FACTORY.createDTD("\n"); + private static final XMLEvent TAB = EVENT_FACTORY.createDTD("\t"); + + private static final Map PREFS + = new HashMap(); + + private static final Map> COLS + = new TreeMap>(); + + /** + * Retrieve a preference from the editor preference map. + * + * @param name0   {@link forge.gui.deckeditor.SCardListViewUtil.EditorPreference} + * @return TableColumnInfo + */ + public static boolean getPref(final EditorPreference name0) { + return PREFS.get(name0); + } + + /** + * Set a preference in the editor preference map. + * + * @param name0   {@link forge.gui.deckeditor.SCardListViewUtil.EditorPreference} + * @param val0   boolean + */ + public static void setPref(final EditorPreference name0, final boolean val0) { + PREFS.put(name0, val0); + } + + /** + * Retrieve a custom column. + * + * @param name0   {@link forge.gui.deckeditor.SCardListViewUtil.CatalogColumnName} + * @return TableColumnInfo + */ + public static TableColumnInfo getColumn(final ColumnName name0) { + return COLS.get(name0); + } + + /** Publicly-accessible save method, to neatly handle exception handling. */ + public static void savePreferences() { + try { save(); } + catch (final Exception e) { e.printStackTrace(); } + } + + /** Publicly-accessible load method, to neatly handle exception handling. */ + public static void loadPreferences() { + try { load(); } + catch (final Exception e) { e.printStackTrace(); } + } + + /** + * + * TODO: Write javadoc for this method. + * + * @param extends InventoryItem + * @param extends DeckBase + */ + private static void save() throws Exception { + final XMLOutputFactory out = XMLOutputFactory.newInstance(); + final XMLEventWriter writer = out.createXMLEventWriter(new FileOutputStream(NewConstants.EDITOR_PREFERENCES_FILE.userPrefLoc)); + + writer.add(EVENT_FACTORY.createStartDocument()); + writer.add(NEWLINE); + writer.add(EVENT_FACTORY.createStartElement("", "", "preferences")); + writer.add(EVENT_FACTORY.createAttribute("type", "editor")); + writer.add(NEWLINE); + + for (final EditorPreference p : PREFS.keySet()) { + writer.add(TAB); + writer.add(EVENT_FACTORY.createStartElement("", "", "pref")); + writer.add(EVENT_FACTORY.createAttribute( + "name", p.toString())); + writer.add(EVENT_FACTORY.createAttribute( + "value", PREFS.get(p).toString())); + writer.add(EVENT_FACTORY.createEndElement("", "", "pref")); + writer.add(NEWLINE); + } + + for (final ColumnName c : COLS.keySet()) { + // If column is not in view, retain previous model index for the next time + // that the column will be in the view. + int index = SColumnUtil.getColumnViewIndex(c); + if (index == -1) { + index = COLS.get(c).getModelIndex(); + } + + writer.add(TAB); + writer.add(EVENT_FACTORY.createStartElement("", "", "col")); + writer.add(EVENT_FACTORY.createAttribute( + ColumnProperty.enumval.toString(), COLS.get(c).getEnumValue())); + writer.add(EVENT_FACTORY.createAttribute( + ColumnProperty.identifier.toString(), COLS.get(c).getIdentifier().toString())); + writer.add(EVENT_FACTORY.createAttribute( + ColumnProperty.show.toString(), String.valueOf(COLS.get(c).isShowing()))); + writer.add(EVENT_FACTORY.createAttribute( + ColumnProperty.sortpriority.toString(), String.valueOf(COLS.get(c).getSortPriority()))); + writer.add(EVENT_FACTORY.createAttribute( + ColumnProperty.sortstate.toString(), String.valueOf(COLS.get(c).getSortState()))); + writer.add(EVENT_FACTORY.createAttribute( + ColumnProperty.width.toString(), String.valueOf(COLS.get(c).getWidth()))); + writer.add(EVENT_FACTORY.createEndElement("", "", "col")); + writer.add(NEWLINE); + } + + writer.add(EVENT_FACTORY.createEndDocument()); + writer.flush(); + writer.close(); + } + + private static void load() throws Exception { + final XMLInputFactory inputFactory = XMLInputFactory.newInstance(); + + PREFS.clear(); + COLS.clear(); + + // read in defaults + loadPrefs(inputFactory.createXMLEventReader(new FileInputStream(NewConstants.EDITOR_PREFERENCES_FILE.defaultLoc))); + + try { + // overwrite defaults with user preferences, if they exist + loadPrefs(inputFactory.createXMLEventReader(new FileInputStream(NewConstants.EDITOR_PREFERENCES_FILE.userPrefLoc))); + } catch (FileNotFoundException e) { + /* ignore; it's ok if this file doesn't exist */ + } finally { + SColumnUtil.attachSortAndDisplayFunctions(); + } + } + + private static void loadPrefs(final XMLEventReader reader) throws XMLStreamException { + XMLEvent event; + StartElement element; + Iterator attributes; + Attribute attribute; + EditorPreference pref; + TableColumnInfo tempcol; + String tagname; + + while (reader.hasNext()) { + event = reader.nextEvent(); + + if (event.isStartElement()) { + element = event.asStartElement(); + tagname = element.getName().getLocalPart(); + + // Assemble preferences + if (tagname.equals("pref")) { + // Retrieve name of pref + attributes = element.getAttributes(); + try { + pref = EditorPreference.valueOf(((Attribute) attributes.next()).getValue()); + + // Add to map + PREFS.put(pref, Boolean.valueOf(((Attribute) attributes.next()).getValue())); + } catch (IllegalArgumentException e) { /* ignore; just don't use */ } + } + // Assemble columns + else if (tagname.equals("col")) { + attributes = element.getAttributes(); + tempcol = new TableColumnInfo(); + + while (attributes.hasNext()) { + attribute = (Attribute) attributes.next(); + if (attribute.getName().toString().equals(ColumnProperty.enumval.toString())) { + try { COLS.put(ColumnName.valueOf(attribute.getValue()), tempcol); } + catch (final Exception e) { /* ignore invalid entries */ } + + tempcol.setEnumValue(attribute.getValue()); + } + else if (attribute.getName().toString().equals(ColumnProperty.identifier.toString())) { + tempcol.setIdentifier(attribute.getValue()); + tempcol.setHeaderValue(attribute.getValue()); + } + else if (attribute.getName().toString().equals(ColumnProperty.width.toString())) { + tempcol.setPreferredWidth(Integer.valueOf(attribute.getValue())); + } + else if (attribute.getName().toString().equals(ColumnProperty.show.toString())) { + tempcol.setShowing(Boolean.valueOf(attribute.getValue())); + } + else if (attribute.getName().toString().equals(ColumnProperty.sortpriority.toString())) { + tempcol.setSortPriority(Integer.valueOf(attribute.getValue())); + } + else if (attribute.getName().toString().equals(ColumnProperty.sortstate.toString())) { + tempcol.setSortState(SortState.valueOf(attribute.getValue().toString())); + } + } + } + } + } + } +} diff --git a/src/main/java/forge/gui/cardlistview/SCardListViewUtil.java b/src/main/java/forge/gui/cardlistview/SCardListViewUtil.java new file mode 100644 index 00000000000..08d2a26cbbf --- /dev/null +++ b/src/main/java/forge/gui/cardlistview/SCardListViewUtil.java @@ -0,0 +1,135 @@ +package forge.gui.cardlistview; + +import javax.swing.ImageIcon; + +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.Iterables; + +import forge.card.CardRules; +import forge.card.CardRulesPredicates; +import forge.gui.deckeditor.views.VCardCatalog; +import forge.gui.deckeditor.views.VCurrentDeck; +import forge.gui.toolbox.FSkin; +import forge.item.PaperCard; +import forge.item.InventoryItem; +import forge.item.ItemPoolView; +import forge.util.Aggregates; +import forge.util.TextUtil; + + +/** + * Static methods for working with top-level editor methods, + * included but not limited to preferences IO, icon generation, + * and stats analysis. + * + *

+ * (S at beginning of class name denotes a static factory.) + * + */ +public final class SCardListViewUtil { + /** An enum to encapsulate metadata for the stats/filter objects. */ + public static enum StatTypes { + TOTAL (FSkin.ZoneImages.ICO_HAND, null, 0), + WHITE (FSkin.ManaImages.IMG_WHITE, CardRulesPredicates.Presets.IS_WHITE, 1), + BLUE (FSkin.ManaImages.IMG_BLUE, CardRulesPredicates.Presets.IS_BLUE, 1), + BLACK (FSkin.ManaImages.IMG_BLACK, CardRulesPredicates.Presets.IS_BLACK, 1), + RED (FSkin.ManaImages.IMG_RED, CardRulesPredicates.Presets.IS_RED, 1), + GREEN (FSkin.ManaImages.IMG_GREEN, CardRulesPredicates.Presets.IS_GREEN, 1), + COLORLESS (FSkin.ManaImages.IMG_COLORLESS, CardRulesPredicates.Presets.IS_COLORLESS, 1), + MULTICOLOR (FSkin.EditorImages.IMG_MULTI, CardRulesPredicates.Presets.IS_MULTICOLOR, 1), + + PACK (FSkin.EditorImages.IMG_PACK, null, 2), + LAND (FSkin.EditorImages.IMG_LAND, CardRulesPredicates.Presets.IS_LAND, 2), + ARTIFACT (FSkin.EditorImages.IMG_ARTIFACT, CardRulesPredicates.Presets.IS_ARTIFACT, 2), + CREATURE (FSkin.EditorImages.IMG_CREATURE, CardRulesPredicates.Presets.IS_CREATURE, 2), + ENCHANTMENT (FSkin.EditorImages.IMG_ENCHANTMENT, CardRulesPredicates.Presets.IS_ENCHANTMENT, 2), + PLANESWALKER (FSkin.EditorImages.IMG_PLANESWALKER, CardRulesPredicates.Presets.IS_PLANESWALKER, 2), + INSTANT (FSkin.EditorImages.IMG_INSTANT, CardRulesPredicates.Presets.IS_INSTANT, 2), + SORCERY (FSkin.EditorImages.IMG_SORCERY, CardRulesPredicates.Presets.IS_SORCERY, 2); + + public final ImageIcon img; + public final Predicate predicate; + public final int group; + + StatTypes(FSkin.SkinProp prop, Predicate pred, int grp) { + img = new ImageIcon(FSkin.getImage(prop, 18, 18)); + predicate = pred; + group = grp; + } + + public String toLabelString() { + if (this == PACK) { + return "Card packs and prebuilt decks"; + } + return TextUtil.enumToLabel(this) + " cards"; + } + } + + /** + * Divides X by Y, multiplies by 100, rounds, returns. + * + * @param x0   Numerator (int) + * @param y0   Denominator (int) + * @return rounded result (int) + */ + public static int calculatePercentage(final int x0, final int y0) { + return (int) Math.round((double) (x0 * 100) / (double) y0); + } + + private static final Predicate totalPred = Predicates.instanceOf(PaperCard.class); + private static final Predicate packPred = Predicates.not(totalPred); + + /** + * setStats. + * + * @param   the generic type + * @param items   ItemPoolView + * @param view   {@link forge.gui.deckeditor.views.ITableContainer} + */ + public static void setStats(final ItemPoolView items, final ITableContainer view) { + for (StatTypes s : StatTypes.values()) { + switch (s) { + case TOTAL: + view.getStatLabel(s).setText(String.valueOf( + Aggregates.sum(Iterables.filter(items, Predicates.compose(totalPred, items.FN_GET_KEY)), items.FN_GET_COUNT))); + break; + case PACK: + view.getStatLabel(s).setText(String.valueOf( + Aggregates.sum(Iterables.filter(items, Predicates.compose(packPred, items.FN_GET_KEY)), items.FN_GET_COUNT))); + break; + default: + view.getStatLabel(s).setText(String.valueOf(items.countAll(Predicates.compose(s.predicate, PaperCard.FN_GET_RULES), PaperCard.class))); + } + } + } + + /** + * Resets components that may have been changed + * by various configurations of the deck editor. + */ + public static void resetUI() { + VCardCatalog.SINGLETON_INSTANCE.getBtnAdd4().setVisible(true); + VCurrentDeck.SINGLETON_INSTANCE.getBtnRemove4().setVisible(true); + + VCurrentDeck.SINGLETON_INSTANCE.getBtnSave().setVisible(true); + VCurrentDeck.SINGLETON_INSTANCE.getBtnSaveAs().setVisible(true); + VCurrentDeck.SINGLETON_INSTANCE.getBtnNew().setVisible(true); + VCurrentDeck.SINGLETON_INSTANCE.getBtnOpen().setVisible(true); + + VCurrentDeck.SINGLETON_INSTANCE.getTxfTitle().setEnabled(true); + + VCardCatalog.SINGLETON_INSTANCE.getPnlHeader().setVisible(false); + VCardCatalog.SINGLETON_INSTANCE.getLblTitle().setText(""); + + VCurrentDeck.SINGLETON_INSTANCE.getPnlHeader().setVisible(true); + + VCardCatalog.SINGLETON_INSTANCE.getTabLabel().setText("Card Catalog"); + + VCurrentDeck.SINGLETON_INSTANCE.getBtnPrintProxies().setVisible(true); + VCurrentDeck.SINGLETON_INSTANCE.getBtnDoSideboard().setVisible(false); + + VCurrentDeck.SINGLETON_INSTANCE.getTxfTitle().setVisible(true); + VCurrentDeck.SINGLETON_INSTANCE.getLblTitle().setText("Title:"); + } +} diff --git a/src/main/java/forge/gui/cardlistview/SColumnUtil.java b/src/main/java/forge/gui/cardlistview/SColumnUtil.java new file mode 100644 index 00000000000..1c694602188 --- /dev/null +++ b/src/main/java/forge/gui/cardlistview/SColumnUtil.java @@ -0,0 +1,585 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Forge Team + * + * 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 3 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, see . + */ +package forge.gui.cardlistview; + +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.Map.Entry; +import java.util.regex.Pattern; + +import javax.swing.JTable; +import javax.swing.table.TableColumn; +import javax.swing.table.TableColumnModel; + +import com.google.common.base.Function; + +import forge.Singletons; +import forge.card.CardAiHints; +import forge.card.CardEdition; +import forge.card.CardRarity; +import forge.card.CardRules; +import forge.card.ColorSet; +import forge.card.mana.ManaCost; +import forge.deck.DeckBase; +import forge.game.limited.DraftRankCache; +import forge.gui.deckeditor.CDeckEditorUI; +import forge.gui.deckeditor.controllers.ACEditorBase; +import forge.item.PaperCard; +import forge.item.IPaperCard; +import forge.item.InventoryItem; +import forge.item.InventoryItemFromSet; + +/** + * A collection of methods pertaining to columns in card catalog and + * current deck tables, for use in the deck editor. + *

+ * (S at beginning of class name denotes a static factory.) + * + */ +public final class SColumnUtil { + /** + * Each catalog column identified in the XML file is + * referenced using these names. Its name in the XML + * should match the name in the enum. Underscores + * will be replaced with spaces in the display. + *

+ * Note: To add a new column, put an enum here, and also add in the XML prefs file. + */ + public enum ColumnName { /** */ + CAT_QUANTITY, /** */ + CAT_NAME, /** */ + CAT_COST, /** */ + CAT_COLOR, /** */ + CAT_TYPE, /** */ + CAT_POWER, /** */ + CAT_TOUGHNESS, /** */ + CAT_CMC, /** */ + CAT_RARITY, /** */ + CAT_SET, /** */ + CAT_AI, /** */ + CAT_NEW, /** */ + CAT_PURCHASE_PRICE, /** */ + CAT_OWNED, /** */ + CAT_RANKING, + DECK_QUANTITY, /** */ + DECK_NAME, /** */ + DECK_COST, /** */ + DECK_COLOR, /** */ + DECK_TYPE, /** */ + DECK_POWER, /** */ + DECK_TOUGHNESS, /** */ + DECK_CMC, /** */ + DECK_RARITY, /** */ + DECK_SET, /** */ + DECK_AI, /** */ + DECK_NEW, /** */ + DECK_SALE_PRICE, /** */ + DECK_DECKS, + DECK_RANKING; + } + + /** Possible states of data sorting in a column: none, ascending, or descending. */ + public enum SortState { /** */ + NONE, /** */ + ASC, /** */ + DESC + } + + /** @return List> */ + public static List> getCatalogDefaultColumns() { + final List> columns = new ArrayList>(); + + columns.add(SColumnUtil.getColumn(ColumnName.CAT_QUANTITY)); + columns.add(SColumnUtil.getColumn(ColumnName.CAT_NAME)); + columns.add(SColumnUtil.getColumn(ColumnName.CAT_COST)); + columns.add(SColumnUtil.getColumn(ColumnName.CAT_COLOR)); + columns.add(SColumnUtil.getColumn(ColumnName.CAT_TYPE)); + columns.add(SColumnUtil.getColumn(ColumnName.CAT_POWER)); + columns.add(SColumnUtil.getColumn(ColumnName.CAT_TOUGHNESS)); + columns.add(SColumnUtil.getColumn(ColumnName.CAT_CMC)); + columns.add(SColumnUtil.getColumn(ColumnName.CAT_RARITY)); + columns.add(SColumnUtil.getColumn(ColumnName.CAT_SET)); + columns.add(SColumnUtil.getColumn(ColumnName.CAT_AI)); + columns.add(SColumnUtil.getColumn(ColumnName.CAT_RANKING)); + + return columns; + } + + /** @return List> */ + public static List> getDeckDefaultColumns() { + final List> columns = new ArrayList>(); + + columns.add(SColumnUtil.getColumn(ColumnName.DECK_QUANTITY)); + columns.add(SColumnUtil.getColumn(ColumnName.DECK_NAME)); + columns.add(SColumnUtil.getColumn(ColumnName.DECK_COST)); + columns.add(SColumnUtil.getColumn(ColumnName.DECK_COLOR)); + columns.add(SColumnUtil.getColumn(ColumnName.DECK_TYPE)); + columns.add(SColumnUtil.getColumn(ColumnName.DECK_POWER)); + columns.add(SColumnUtil.getColumn(ColumnName.DECK_TOUGHNESS)); + columns.add(SColumnUtil.getColumn(ColumnName.DECK_CMC)); + columns.add(SColumnUtil.getColumn(ColumnName.DECK_RARITY)); + columns.add(SColumnUtil.getColumn(ColumnName.DECK_SET)); + columns.add(SColumnUtil.getColumn(ColumnName.DECK_AI)); + columns.add(SColumnUtil.getColumn(ColumnName.DECK_RANKING)); + + return columns; + } + + /** Should be called after column preferences has run, which has created a new column list. */ + public static void attachSortAndDisplayFunctions() { + SColumnUtil.getColumn(ColumnName.CAT_QUANTITY).setSortAndDisplayFunctions( + SColumnUtil.FN_QTY_COMPARE, SColumnUtil.FN_QTY_GET); + SColumnUtil.getColumn(ColumnName.CAT_NAME).setSortAndDisplayFunctions( + SColumnUtil.FN_NAME_COMPARE, SColumnUtil.FN_NAME_GET); + SColumnUtil.getColumn(ColumnName.CAT_COST).setSortAndDisplayFunctions( + SColumnUtil.FN_COST_COMPARE, SColumnUtil.FN_COST_GET); + SColumnUtil.getColumn(ColumnName.CAT_COLOR).setSortAndDisplayFunctions( + SColumnUtil.FN_COLOR_COMPARE, SColumnUtil.FN_COLOR_GET); + SColumnUtil.getColumn(ColumnName.CAT_TYPE).setSortAndDisplayFunctions( + SColumnUtil.FN_TYPE_COMPARE, SColumnUtil.FN_TYPE_GET); + SColumnUtil.getColumn(ColumnName.CAT_POWER).setSortAndDisplayFunctions( + SColumnUtil.FN_POWER_COMPARE, SColumnUtil.FN_POWER_GET); + SColumnUtil.getColumn(ColumnName.CAT_TOUGHNESS).setSortAndDisplayFunctions( + SColumnUtil.FN_TOUGHNESS_COMPARE, SColumnUtil.FN_TOUGHNESS_GET); + SColumnUtil.getColumn(ColumnName.CAT_CMC).setSortAndDisplayFunctions( + SColumnUtil.FN_CMC_COMPARE, SColumnUtil.FN_CMC_GET); + SColumnUtil.getColumn(ColumnName.CAT_RARITY).setSortAndDisplayFunctions( + SColumnUtil.FN_RARITY_COMPARE, SColumnUtil.FN_RARITY_GET); + SColumnUtil.getColumn(ColumnName.CAT_SET).setSortAndDisplayFunctions( + SColumnUtil.FN_SET_COMPARE, SColumnUtil.FN_SET_GET); + SColumnUtil.getColumn(ColumnName.CAT_AI).setSortAndDisplayFunctions( + SColumnUtil.FN_AI_STATUS_COMPARE, SColumnUtil.FN_AI_STATUS_GET); + SColumnUtil.getColumn(ColumnName.CAT_RANKING).setSortAndDisplayFunctions( + SColumnUtil.FN_RANKING_COMPARE, SColumnUtil.FN_RANKING_GET); + + SColumnUtil.getColumn(ColumnName.DECK_QUANTITY).setSortAndDisplayFunctions( + SColumnUtil.FN_QTY_COMPARE, SColumnUtil.FN_QTY_GET); + SColumnUtil.getColumn(ColumnName.DECK_NAME).setSortAndDisplayFunctions( + SColumnUtil.FN_NAME_COMPARE, SColumnUtil.FN_NAME_GET); + SColumnUtil.getColumn(ColumnName.DECK_COST).setSortAndDisplayFunctions( + SColumnUtil.FN_COST_COMPARE, SColumnUtil.FN_COST_GET); + SColumnUtil.getColumn(ColumnName.DECK_COLOR).setSortAndDisplayFunctions( + SColumnUtil.FN_COLOR_COMPARE, SColumnUtil.FN_COLOR_GET); + SColumnUtil.getColumn(ColumnName.DECK_TYPE).setSortAndDisplayFunctions( + SColumnUtil.FN_TYPE_COMPARE, SColumnUtil.FN_TYPE_GET); + SColumnUtil.getColumn(ColumnName.DECK_POWER).setSortAndDisplayFunctions( + SColumnUtil.FN_POWER_COMPARE, SColumnUtil.FN_POWER_GET); + SColumnUtil.getColumn(ColumnName.DECK_TOUGHNESS).setSortAndDisplayFunctions( + SColumnUtil.FN_TOUGHNESS_COMPARE, SColumnUtil.FN_TOUGHNESS_GET); + SColumnUtil.getColumn(ColumnName.DECK_CMC).setSortAndDisplayFunctions( + SColumnUtil.FN_CMC_COMPARE, SColumnUtil.FN_CMC_GET); + SColumnUtil.getColumn(ColumnName.DECK_RARITY).setSortAndDisplayFunctions( + SColumnUtil.FN_RARITY_COMPARE, SColumnUtil.FN_RARITY_GET); + SColumnUtil.getColumn(ColumnName.DECK_SET).setSortAndDisplayFunctions( + SColumnUtil.FN_SET_COMPARE, SColumnUtil.FN_SET_GET); + SColumnUtil.getColumn(ColumnName.DECK_AI).setSortAndDisplayFunctions( + SColumnUtil.FN_AI_STATUS_COMPARE, SColumnUtil.FN_AI_STATUS_GET); + SColumnUtil.getColumn(ColumnName.DECK_RANKING).setSortAndDisplayFunctions( + SColumnUtil.FN_RANKING_COMPARE, SColumnUtil.FN_RANKING_GET); + + SColumnUtil.getColumn(ColumnName.CAT_COST).setCellRenderer(new ManaCostRenderer()); + SColumnUtil.getColumn(ColumnName.CAT_POWER).setCellRenderer(new IntegerRenderer()); + SColumnUtil.getColumn(ColumnName.CAT_TOUGHNESS).setCellRenderer(new IntegerRenderer()); + SColumnUtil.getColumn(ColumnName.CAT_CMC).setCellRenderer(new IntegerRenderer()); + SColumnUtil.getColumn(ColumnName.CAT_SET).setCellRenderer(new SetCodeRenderer()); + + SColumnUtil.getColumn(ColumnName.DECK_COST).setCellRenderer(new ManaCostRenderer()); + SColumnUtil.getColumn(ColumnName.DECK_POWER).setCellRenderer(new IntegerRenderer()); + SColumnUtil.getColumn(ColumnName.DECK_TOUGHNESS).setCellRenderer(new IntegerRenderer()); + SColumnUtil.getColumn(ColumnName.DECK_CMC).setCellRenderer(new IntegerRenderer()); + SColumnUtil.getColumn(ColumnName.DECK_SET).setCellRenderer(new SetCodeRenderer()); + } + + /** + * Hides/shows a table column. + * + * @param col0 TableColumnInfo + * @param extends InventoryItem + * @param extends DeckBase + */ + @SuppressWarnings("unchecked") + public static + void toggleColumn(final TableColumnInfo col0) { + + final ACEditorBase ed = (ACEditorBase) + CDeckEditorUI.SINGLETON_INSTANCE.getCurrentEditorController(); + + final JTable tbl = (col0.getEnumValue().substring(0, 4).equals("DECK")) + ? ed.getTableDeck().getTable() + : ed.getTableCatalog().getTable(); + + final TableColumnModel colmodel = tbl.getColumnModel(); + + if (col0.isShowing()) { + col0.setShowing(false); + colmodel.removeColumn(col0); + } + else { + col0.setShowing(true); + colmodel.addColumn(col0); + + if (col0.getModelIndex() < colmodel.getColumnCount()) { + colmodel.moveColumn(colmodel.getColumnIndex(col0.getIdentifier()), col0.getModelIndex()); + Enumeration cols = colmodel.getColumns(); + int index = 0; + // If you're getting renderer "can't cast T to U" errors, that's + // a sign that the model index needs updating. + while (cols.hasMoreElements()) { + cols.nextElement().setModelIndex(index++); + } + } + else { + col0.setModelIndex(colmodel.getColumnCount()); + } + } + } + + /** + * Retrieve a custom column (uses identical method in SEditorIO). + * + * @param id0   {@link forge.gui.deckeditor.SCardListViewUtil.CatalogColumnName} + * @return TableColumnInfo + */ + public static TableColumnInfo getColumn(final ColumnName id0) { + return SCardListViewIO.getColumn(id0); + } + + /** + * Convenience method to get a column's index in the view (that is, + * in the TableColumnModel). + * + * @param id0   {@link forge.gui.deckeditor.SCardListViewUtil.CatalogColumnName} + * @return int + * @param extends InventoryItem + * @param extends InventoryItem + */ + @SuppressWarnings("unchecked") + public static + int getColumnViewIndex(final ColumnName id0) { + + final ACEditorBase ed = (ACEditorBase) + CDeckEditorUI.SINGLETON_INSTANCE.getCurrentEditorController(); + + final JTable tbl = (id0.toString().substring(0, 4).equals("DECK")) + ? ed.getTableDeck().getTable() + : ed.getTableCatalog().getTable(); + + int index = -1; + + try { + index = tbl.getColumnModel().getColumnIndex(SColumnUtil.getColumn(id0).getIdentifier()); + } + catch (final Exception e) { } + + return index; + } + + /** + * Convenience method to get a column's index in the model (that is, + * in the EditorTableModel, NOT the TableColumnModel). + * + * @param id0   {@link forge.gui.deckeditor.SCardListViewUtil.CatalogColumnName} + * @return int + * @param extends InventoryItem + * @param extends InventoryItem + */ + @SuppressWarnings("unchecked") + public static + int getColumnModelIndex(final ColumnName id0) { + + final ACEditorBase ed = (ACEditorBase) + CDeckEditorUI.SINGLETON_INSTANCE.getCurrentEditorController(); + + final JTable tbl = (id0.toString().substring(0, 4).equals("DECK")) + ? ed.getTableDeck().getTable() + : ed.getTableCatalog().getTable(); + + return tbl.getColumn(SColumnUtil.getColumn(id0).getIdentifier()).getModelIndex(); + } + + //========== Display functions + + private static final Pattern AE_FINDER = Pattern.compile("AE", Pattern.LITERAL); + + private static ManaCost toManaCost(final InventoryItem i) { + return i instanceof IPaperCard ? ((IPaperCard) i).getRules().getManaCost() : ManaCost.NO_COST; + } + private static CardRules toCardRules(final InventoryItem i) { + return i instanceof IPaperCard ? ((IPaperCard) i).getRules() : null; + } + + private static ColorSet toColor(final InventoryItem i) { + return i instanceof IPaperCard ? ((IPaperCard) i).getRules().getColor() : ColorSet.getNullColor(); + } + + private static int toPower(final InventoryItem i) { + int result = -1; + if (i instanceof PaperCard) { + result = ((IPaperCard) i).getRules().getIntPower(); + if (result == -1) { + result = ((IPaperCard) i).getRules().getInitialLoyalty(); + } + } + return result; + } + + private static int toToughness(final InventoryItem i) { + return i instanceof PaperCard ? ((IPaperCard) i).getRules().getIntToughness() : -1; + } + + private static Integer toCMC(final InventoryItem i) { + return i instanceof PaperCard ? ((IPaperCard) i).getRules().getManaCost().getCMC() : -1; + } + + private static CardRarity toRarity(final InventoryItem i) { + return i instanceof PaperCard ? ((IPaperCard) i).getRarity() : CardRarity.Unknown; + } + + private static CardEdition toSetCmp(final InventoryItem i) { + return i instanceof InventoryItemFromSet ? Singletons.getModel().getEditions() + .get(((InventoryItemFromSet) i).getEdition()) : CardEdition.UNKNOWN; + } + + private static String toSetStr(final InventoryItem i) { + return i instanceof InventoryItemFromSet ? ((InventoryItemFromSet) i).getEdition() : "n/a"; + } + + private static Integer toAiCmp(final InventoryItem i) { + return i instanceof PaperCard ? ((IPaperCard) i).getRules().getAiHints().getAiStatusComparable() : Integer.valueOf(-1); + } + + private static String toAiStr(final InventoryItem i) { + if (!(i instanceof PaperCard)) + return "n/a"; + + IPaperCard cp = (IPaperCard) i; + CardAiHints ai = cp.getRules().getAiHints(); + + return ai.getRemAIDecks() ? (ai.getRemRandomDecks() ? "AI ?" : "AI") + : (ai.getRemRandomDecks() ? "?" : ""); + } + + private static Double toRankingCmp(final InventoryItem i) { + Double ranking = 500D; + if (i != null && i instanceof PaperCard){ + PaperCard cp = (PaperCard) i; + ranking = DraftRankCache.getRanking(cp.getName(), cp.getEdition()); + if ( ranking == null ) + ranking = 500D; + } + return ranking; + } + + //========== + + /** Lamda sort fnQtyCompare. */ + private static final Function, Comparable> FN_QTY_COMPARE = new Function, Comparable>() { + @Override + public Comparable apply(final Entry from) { + return from.getValue(); + } + }; + + /** Lamda sort fnQtyGet. */ + private static final Function, Object> FN_QTY_GET = new Function, Object>() { + @Override + public Object apply(final Entry from) { + return from.getValue(); + } + }; + + /** Lamda sort fnNameCompare. */ + private static final Function, Comparable> FN_NAME_COMPARE = new Function, Comparable>() { + @Override + public Comparable apply(final Entry from) { + return from.getKey().getName(); + } + }; + + /** Lamda sort fnNameGet. */ + private static final Function, Object> FN_NAME_GET = new Function, Object>() { + @Override + public Object apply(final Entry from) { + final String name = from.getKey().getName(); + return name.contains("AE") ? SColumnUtil.AE_FINDER.matcher(name).replaceAll("\u00C6") : name; + } + }; + + /** Lamda sort fnCostCompare. */ + private static final Function, Comparable> FN_COST_COMPARE = new Function, Comparable>() { + @Override + public Comparable apply(final Entry from) { + return SColumnUtil.toManaCost(from.getKey()); + } + }; + + /** Lamda sort fnCostGet. */ + private static final Function, Object> FN_COST_GET = new Function, Object>() { + @Override + public Object apply(final Entry from) { + return SColumnUtil.toCardRules(from.getKey()); + } + }; + + /** Lamda sort fnColorCompare. */ + private static final Function, Comparable> FN_COLOR_COMPARE = new Function, Comparable>() { + @Override + public Comparable apply(final Entry from) { + return SColumnUtil.toColor(from.getKey()); + } + }; + + /** Lamda sort fnColorGet. */ + private static final Function, Object> FN_COLOR_GET = new Function, Object>() { + @Override + public Object apply(final Entry from) { + return SColumnUtil.toColor(from.getKey()); + } + }; + + /** Lamda sort fnTypeCompare. */ + private static final Function, Comparable> FN_TYPE_COMPARE = new Function, Comparable>() { + @Override + public Comparable apply(final Entry from) { + InventoryItem i = from.getKey(); + return i instanceof PaperCard ? ((IPaperCard)i).getRules().getType().toString() : i.getItemType(); + } + }; + + /** Lamda sort fnTypeGet. */ + private static final Function, Object> FN_TYPE_GET = new Function, Object>() { + @Override + public Object apply(final Entry from) { + InventoryItem i = from.getKey(); + return i instanceof PaperCard ? ((IPaperCard)i).getRules().getType().toString() : i.getItemType(); + } + }; + + /** Lamda sort fnPowerCompare. */ + private static final Function, Comparable> FN_POWER_COMPARE = new Function, Comparable>() { + @Override + public Comparable apply(final Entry from) { + return Integer.valueOf(SColumnUtil.toPower(from.getKey())); + } + }; + + /** Lamda sort fnPowerGet. */ + private static final Function, Object> FN_POWER_GET = new Function, Object>() { + @Override + public Object apply(final Entry from) { + return Integer.valueOf(SColumnUtil.toPower(from.getKey())); + } + }; + + /** Lamda sort fnToughnessCompare. */ + private static final Function, Comparable> FN_TOUGHNESS_COMPARE = new Function, Comparable>() { + @Override + public Comparable apply(final Entry from) { + return Integer.valueOf(SColumnUtil.toToughness(from.getKey())); + } + }; + + /** Lamda sort fnToughnessGet. */ + private static final Function, Object> FN_TOUGHNESS_GET = new Function, Object>() { + @Override + public Object apply(final Entry from) { + return Integer.valueOf(SColumnUtil.toToughness(from.getKey())); + } + }; + + /** Lamda sort fnCMCCompare. */ + private static final Function, Comparable> FN_CMC_COMPARE = new Function, Comparable>() { + @Override + public Comparable apply(final Entry from) { + return SColumnUtil.toCMC(from.getKey()); + } + }; + + /** Lamda sort fnCMCGet. */ + private static final Function, Object> FN_CMC_GET = new Function, Object>() { + @Override + public Object apply(final Entry from) { + return SColumnUtil.toCMC(from.getKey()); + } + }; + + /** Lamda sort fnRarityCompare. */ + private static final Function, Comparable> FN_RARITY_COMPARE = new Function, Comparable>() { + @Override + public Comparable apply(final Entry from) { + return SColumnUtil.toRarity(from.getKey()); + } + }; + + /** Lamda sort fnRarityGet. */ + private static final Function, Object> FN_RARITY_GET = new Function, Object>() { + @Override + public Object apply(final Entry from) { + return SColumnUtil.toRarity(from.getKey()); + } + }; + + /** Lamda sort fnSetCompare. */ + private static final Function, Comparable> FN_SET_COMPARE = new Function, Comparable>() { + @Override + public Comparable apply(final Entry from) { + return SColumnUtil.toSetCmp(from.getKey()); + } + }; + + /** Lamda sort fnSetGet. */ + private static final Function, Object> FN_SET_GET = new Function, Object>() { + @Override + public Object apply(final Entry from) { + return SColumnUtil.toSetStr(from.getKey()); + } + }; + + /** Lamda sort fnAiStatusCompare. */ + private static final Function, Comparable> FN_AI_STATUS_COMPARE = new Function, Comparable>() { + @Override + public Comparable apply(final Entry from) { + return SColumnUtil.toAiCmp(from.getKey()); + } + }; + + /** Lamda sort fnAiStatusGet. */ + private static final Function, Object> FN_AI_STATUS_GET = new Function, Object>() { + @Override + public Object apply(final Entry from) { + return SColumnUtil.toAiStr(from.getKey()); + } + }; + + /** Lamda sort fnRankingCompare. */ + private static final Function, Comparable> FN_RANKING_COMPARE = new Function, Comparable>() { + @Override + public Comparable apply(final Entry from) { + return SColumnUtil.toRankingCmp(from.getKey()); + } + }; + + /** Lamda sort fnRankingGet. */ + private static final Function, Object> FN_RANKING_GET = new Function, Object>() { + @Override + public Object apply(final Entry from) { + return String.valueOf(SColumnUtil.toRankingCmp(from.getKey())); + } + }; +} diff --git a/src/main/java/forge/gui/cardlistview/SFilterUtil.java b/src/main/java/forge/gui/cardlistview/SFilterUtil.java new file mode 100644 index 00000000000..d7a01a7d08c --- /dev/null +++ b/src/main/java/forge/gui/cardlistview/SFilterUtil.java @@ -0,0 +1,144 @@ +package forge.gui.cardlistview; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.apache.commons.lang3.tuple.Pair; + +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; + +import forge.card.CardRules; +import forge.card.CardRulesPredicates; +import forge.gui.deckeditor.views.VCardCatalog; +import forge.gui.deckeditor.views.VCardCatalog.RangeTypes; +import forge.gui.toolbox.FLabel; +import forge.gui.toolbox.FSpinner; +import forge.item.PaperCard; +import forge.util.ComparableOp; +import forge.util.PredicateString.StringOp; + +/** + * Static factory; holds blocks of form elements and predicates + * which are used in various editing environments. + *

+ * (S at beginning of class name denotes a static factory.) + */ +public class SFilterUtil { + /** + * queries color filters for state and returns a predicate. + *

+ * Handles "multicolor" label, which is quite tricky. + */ + public static Predicate buildColorAndTypeFilter(Map statLabels) { + final List> colors = new ArrayList>(); + final List> types = new ArrayList>(); + + boolean wantMulticolor = false; + Predicate preExceptMulti = null; + for (SCardListViewUtil.StatTypes s : SCardListViewUtil.StatTypes.values()) { + switch (s) { + case WHITE: case BLUE: case BLACK: case RED: case GREEN: case COLORLESS: + if (statLabels.get(s).getSelected()) { colors.add(s.predicate); } + break; + case MULTICOLOR: + wantMulticolor = statLabels.get(s).getSelected(); + preExceptMulti = wantMulticolor ? null : Predicates.not(s.predicate); + break; + case LAND: case ARTIFACT: case CREATURE: case ENCHANTMENT: case PLANESWALKER: case INSTANT: case SORCERY: + if (statLabels.get(s).getSelected()) { types.add(s.predicate); } + break; + + case TOTAL: case PACK: + // ignore + break; + + default: + throw new RuntimeException("unhandled enum value: " + s); + } + } + + Predicate preColors = colors.size() == 6 ? null : Predicates.or(colors); + Predicate preFinal = colors.isEmpty() && wantMulticolor ? + CardRulesPredicates.Presets.IS_MULTICOLOR : optimizedAnd(preExceptMulti, preColors); + + if (null == preFinal && 7 == types.size()) { + return Predicates.alwaysTrue(); + } + + Predicate typesFinal = Predicates.compose(Predicates.or(types), PaperCard.FN_GET_RULES); + if (null == preFinal) { + return typesFinal; + } + + Predicate colorFinal = Predicates.compose(preFinal, PaperCard.FN_GET_RULES); + if (7 == types.size()) { + return colorFinal; + } + + return Predicates.and(colorFinal, typesFinal); + } + + /** + * builds a string search filter + */ + public static Predicate buildTextFilter(String text, boolean invert, boolean inName, boolean inType, boolean inText) { + if (text.trim().isEmpty()) { + return Predicates.alwaysTrue(); + } + + String[] splitText = text.replaceAll(",", "").replaceAll(" ", " ").split(" "); + + List> terms = new ArrayList>(); + for (String s : splitText) { + List> subands = new ArrayList>(); + + if (inName) { subands.add(CardRulesPredicates.name(StringOp.CONTAINS_IC, s)); } + if (inType) { subands.add(CardRulesPredicates.joinedType(StringOp.CONTAINS_IC, s)); } + if (inText) { subands.add(CardRulesPredicates.rules(StringOp.CONTAINS_IC, s)); } + + terms.add(Predicates.or(subands)); + } + Predicate textFilter = invert ? Predicates.not(Predicates.or(terms)) : Predicates.and(terms); + + return Predicates.compose(textFilter, PaperCard.FN_GET_RULES); + } + + private static Predicate getCardRulesFieldPredicate(int min, int max, CardRulesPredicates.LeafNumber.CardField field) { + boolean hasMin = 0 != min; + boolean hasMax = 10 != max; + + Predicate pMin = !hasMin ? null : new CardRulesPredicates.LeafNumber(field, ComparableOp.GT_OR_EQUAL, min); + Predicate pMax = !hasMax ? null : new CardRulesPredicates.LeafNumber(field, ComparableOp.LT_OR_EQUAL, max); + + return optimizedAnd(pMin, pMax); + } + + private static Predicate optimizedAnd(Predicate p1, Predicate p2) + { + return p1 == null ? p2 : (p2 == null ? p1 : Predicates.and(p1, p2)); + } + + /** + * builds a filter for an interval on a card field + */ + public static Predicate buildIntervalFilter( + Map> spinners, VCardCatalog.RangeTypes field) { + Pair sPair = spinners.get(field); + Predicate fieldFilter = getCardRulesFieldPredicate( + Integer.valueOf(sPair.getLeft().getValue().toString()), + Integer.valueOf(sPair.getRight().getValue().toString()), field.cardField); + + if (null != fieldFilter && VCardCatalog.RangeTypes.CMC != field) + { + fieldFilter = Predicates.and(fieldFilter, CardRulesPredicates.Presets.IS_CREATURE); + } + + if (fieldFilter == null) { + return Predicates.alwaysTrue(); + } else { + return Predicates.compose(fieldFilter, PaperCard.FN_GET_RULES); + } + } +} diff --git a/src/main/java/forge/gui/cardlistview/SetCodeRenderer.java b/src/main/java/forge/gui/cardlistview/SetCodeRenderer.java new file mode 100644 index 00000000000..08b8200f84d --- /dev/null +++ b/src/main/java/forge/gui/cardlistview/SetCodeRenderer.java @@ -0,0 +1,43 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Forge Team + * + * 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 3 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, see . + */ +package forge.gui.cardlistview; + +import javax.swing.table.DefaultTableCellRenderer; + +import forge.Singletons; +import forge.card.EditionCollection; + +/** + * A wrapper to show explanatory tooltips for edition set abbreviations. + */ +@SuppressWarnings("serial") +public class SetCodeRenderer extends DefaultTableCellRenderer implements AlwaysShowToolTip { + @Override + public String getToolTipText() { + String setAbbrev = getText(); + String setFullName = "Unknown set"; + + EditionCollection editions = Singletons.getModel().getEditions(); + + if (null != setAbbrev && editions.contains(setAbbrev)) { + setFullName = editions.get(setAbbrev).getName(); + } + + return String.format("%s (%s)", setFullName, setAbbrev); + } +} diff --git a/src/main/java/forge/gui/cardlistview/TableColumnInfo.java b/src/main/java/forge/gui/cardlistview/TableColumnInfo.java new file mode 100644 index 00000000000..210ba40d444 --- /dev/null +++ b/src/main/java/forge/gui/cardlistview/TableColumnInfo.java @@ -0,0 +1,143 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Forge Team + * + * 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 3 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, see . + */ +package forge.gui.cardlistview; + +import java.util.Map.Entry; + +import javax.swing.table.TableColumn; + +import com.google.common.base.Function; + +import forge.gui.deckeditor.tables.SColumnUtil.SortState; + +/** + * A column object in a EditorTableModel in the card editor. + * Requires a sorting function and a display function + * (to extract information as appropriate for table row data). + * + * @param a generic type + */ + +public class TableColumnInfo extends TableColumn { + private static final long serialVersionUID = 3749431834643427572L; + private SortState sortstate = SortState.NONE; + private int sortPriority = 0; + private boolean show = true; + private String enumval; + + private Function, Comparable> fnSort; + private Function, Object> fnDisplay; + + /** */ + public TableColumnInfo() { + super(); + } + + /** + * Unique identifier in SColumnUtil.ColumnName enum. + * + * @return {@link java.lang.String} + */ + public String getEnumValue() { + return enumval; + } + + /** + * Unique identifier in SColumnUtil.ColumnName enum. + * + * @param val0   {@link java.lang.String} + */ + public void setEnumValue(final String val0) { + this.enumval = val0; + } + + /** + * Position in sort cascade, 0 for no priority. + * + * @return int + */ + public int getSortPriority() { + return sortPriority; + } + + /** + * Position in sort cascade, 0 for no priority. + * + * @param position0   int + */ + public void setSortPriority(final int position0) { + this.sortPriority = position0; + } + + /** @return {@link forge.gui.deckeditor.tables.EditorTableModel.SortState} */ + public SortState getSortState() { + return this.sortstate; + } + + /** @param state0   {@link forge.gui.deckeditor.tables.TableColumnInfo.SortState} */ + public void setSortState(final SortState state0) { + this.sortstate = state0; + } + + /** @return boolean */ + public boolean isShowing() { + return this.show; + } + + /** @param boolean0   show/hide this column */ + public void setShowing(final boolean boolean0) { + this.show = boolean0; + } + + /** + * Lambda closure used to sort this column. + * + * @return the fnSort + */ + public Function, Comparable> getFnSort() { + if (fnSort == null) { + throw new NullPointerException("A sort function hasn't been set for " + + "Column " + TableColumnInfo.this.getIdentifier()); + } + return this.fnSort; + } + + /** + * Gets the fn display. + * + * @return the fnDisplay + */ + public Function, Object> getFnDisplay() { + if (fnSort == null) { + throw new NullPointerException("A display function hasn't been set for " + + "Column " + TableColumnInfo.this.getIdentifier()); + } + return this.fnDisplay; + } + + /** + * Lambda closure used to sort this column, and fn display. + * + * @param lambda0 the fnSort + * @param lambda1 the fnDisplay + */ + public void setSortAndDisplayFunctions(final Function, Comparable> lambda0, final Function, Object> lambda1) { + this.fnSort = lambda0; + this.fnDisplay = lambda1; + } +} diff --git a/src/main/java/forge/gui/cardlistview/TableSorter.java b/src/main/java/forge/gui/cardlistview/TableSorter.java new file mode 100644 index 00000000000..ab0d42086f6 --- /dev/null +++ b/src/main/java/forge/gui/cardlistview/TableSorter.java @@ -0,0 +1,86 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Forge Team + * + * 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 3 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, see . + */ +package forge.gui.cardlistview; + +import java.util.Comparator; +import java.util.Map.Entry; + +import com.google.common.base.Function; + +import forge.item.PaperCard; + +/** + *

+ * TableSorter class. + *

+ * + * @param + * the generic type + * @author Forge + * @version $Id: TableSorter.java 21966 2013-06-05 06:58:32Z Max mtg $ + */ +@SuppressWarnings("unchecked") +// Comparable needs +public class TableSorter implements Comparator> { + private final boolean ascending; + private final Function, Comparable> field; + + /** + *

+ * Constructor for TableSorter. + *

+ * + * @param field + * the field + * @param inAscending + * a boolean. + */ + public TableSorter(final Function, Comparable> field, final boolean inAscending) { + this.field = field; + this.ascending = inAscending; + } + + /** The Constant byNameThenSet. */ + public static final TableSorter BY_NAME_THEN_SET = new TableSorter( + new Function, Comparable>() { + @Override + public Comparable apply(final Entry from) { + return from.getKey(); + } + }, true); + + /* + * (non-Javadoc) + * + * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) + */ + @SuppressWarnings("rawtypes") + @Override + public final int compare(final Entry arg0, final Entry arg1) { + final Comparable obj1 = this.field.apply(arg0); + final Comparable obj2 = this.field.apply(arg1); + if (obj1 == null) { + return -1; + } + if (obj2 == null) { + return 1; + } + //System.out.println(String.format("%s vs %s _______ %s vs %s", arg0, arg1, obj1, obj2)); + return this.ascending ? obj1.compareTo(obj2) : obj2.compareTo(obj1); + } +} diff --git a/src/main/java/forge/gui/cardlistview/TableSorterCascade.java b/src/main/java/forge/gui/cardlistview/TableSorterCascade.java new file mode 100644 index 00000000000..2f1e67b5f42 --- /dev/null +++ b/src/main/java/forge/gui/cardlistview/TableSorterCascade.java @@ -0,0 +1,71 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Forge Team + * + * 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 3 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, see . + */ +package forge.gui.cardlistview; + +import java.util.Comparator; +import java.util.List; +import java.util.Map.Entry; + +import forge.item.InventoryItem; + +/** + *

+ * TableSorter class. + *

+ * + * @param + * extends InventoryItem + * @author Forge + * @version $Id: TableSorter.java 10146 2011-09-01 18:11:00Z Max mtg $ + */ +public class TableSorterCascade implements Comparator> { + private final List> sorters; + private final int cntFields; + + /** + * + * TableSorterCascade Constructor. + * + * @param sortersCascade + * a List> + */ + public TableSorterCascade(final List> sortersCascade) { + this.sorters = sortersCascade; + this.cntFields = sortersCascade.size(); + } + + /* + * (non-Javadoc) + * + * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) + */ + @Override + public final int compare(final Entry arg0, final Entry arg1) { + int lastCompare = 0; + int iField = -1; + while ((++iField < this.cntFields) && (lastCompare == 0)) { // reverse + // iteration + final TableSorter sorter = this.sorters.get(iField); + if (sorter == null) { + break; + } + lastCompare = sorter.compare(arg0, arg1); + } + return lastCompare; + } +} diff --git a/src/main/java/forge/gui/cardlistview/package-info.java b/src/main/java/forge/gui/cardlistview/package-info.java new file mode 100644 index 00000000000..91364be61a6 --- /dev/null +++ b/src/main/java/forge/gui/cardlistview/package-info.java @@ -0,0 +1,3 @@ +/** Forge Card Game. */ +package forge.gui.cardlistview; +