From a6993eae8ed82a58ac40ee1a7210f32b57d899b8 Mon Sep 17 00:00:00 2001 From: Krazy Date: Sat, 24 Jun 2017 04:57:11 +0000 Subject: [PATCH] Add shift+click multi-select to custom quest format dialog --- .../screens/home/quest/DialogChooseSets.java | 237 ++++++++++-------- .../java/forge/toolbox/FCheckBoxList.java | 203 +++++++++------ 2 files changed, 258 insertions(+), 182 deletions(-) diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/quest/DialogChooseSets.java b/forge-gui-desktop/src/main/java/forge/screens/home/quest/DialogChooseSets.java index de9aadaa197..37a013c0cda 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/home/quest/DialogChooseSets.java +++ b/forge-gui-desktop/src/main/java/forge/screens/home/quest/DialogChooseSets.java @@ -17,136 +17,155 @@ import java.util.Collections; import java.util.List; public class DialogChooseSets { - private final List selectedSets = new ArrayList<>(); - private boolean wantReprints = true; - private Runnable okCallback; + private final List selectedSets = new ArrayList<>(); + private boolean wantReprints = true; + private Runnable okCallback; - private final List choices = new ArrayList<>(); - private final FCheckBox cbWantReprints = new FCheckBox("Allow compatible reprints from other sets"); + private final List choices = new ArrayList<>(); + private final FCheckBox cbWantReprints = new FCheckBox("Allow compatible reprints from other sets"); - // lists are of set codes (e.g. "2ED") - public DialogChooseSets( - Collection preselectedSets, Collection unselectableSets, boolean showWantReprintsCheckbox) { + // lists are of set codes (e.g. "2ED") + public DialogChooseSets( + Collection preselectedSets, Collection unselectableSets, boolean showWantReprintsCheckbox) { - // create a local copy of the editions list so we can sort it - List editions = new ArrayList<>(); - for (CardEdition ce : FModel.getMagicDb().getEditions()) { - editions.add(ce); - } - Collections.sort(editions); - Collections.reverse(editions); + // create a local copy of the editions list so we can sort it + List editions = new ArrayList<>(); + for (CardEdition ce : FModel.getMagicDb().getEditions()) { + editions.add(ce); + } + Collections.sort(editions); + Collections.reverse(editions); - List coreSets = new ArrayList<>(); - List expansionSets = new ArrayList<>(); - List otherSets = new ArrayList<>(); + List coreSets = new ArrayList<>(); + List expansionSets = new ArrayList<>(); + List otherSets = new ArrayList<>(); - for (CardEdition ce : editions) { - String code = ce.getCode(); - FCheckBox box = new FCheckBox(String.format("%s (%s)", ce.getName(), code)); - box.setName(code); - box.setSelected(null != preselectedSets && preselectedSets.contains(code)); - box.setEnabled(null == unselectableSets || !unselectableSets.contains(code)); - switch (ce.getType()) { - case CORE: coreSets.add(box); break; - case EXPANSION: expansionSets.add(box); break; - default: otherSets.add(box); break; - } - } + for (CardEdition ce : editions) { - FPanel p = new FPanel(new MigLayout("insets 0, gap 0, center, wrap 3")); - p.setOpaque(false); - p.setBackgroundTexture(FSkin.getIcon(FSkinProp.BG_TEXTURE)); + String code = ce.getCode(); + FCheckBox box = new FCheckBox(String.format("%s (%s)", ce.getName(), code)); - p.add(new FLabel.Builder().text("Choose sets").fontSize(18).build(), "center, span, wrap, gaptop 10"); + box.setName(code); + box.setSelected(null != preselectedSets && preselectedSets.contains(code)); + box.setEnabled(null == unselectableSets || !unselectableSets.contains(code)); - String constraints = "aligny top"; - p.add(makeCheckBoxList(coreSets, "Core sets", true), constraints); - p.add(makeCheckBoxList(expansionSets, "Expansions", false), constraints); - p.add(makeCheckBoxList(otherSets, "Other sets", false), constraints); + switch (ce.getType()) { + case CORE: + coreSets.add(box); + break; + case EXPANSION: + expansionSets.add(box); + break; + default: + otherSets.add(box); + break; + } - final JPanel overlay = FOverlay.SINGLETON_INSTANCE.getPanel(); - overlay.setLayout(new MigLayout("insets 0, gap 0, wrap, ax center, ay center")); + } - final Runnable cleanup = new Runnable() { - @Override - public void run() { - SOverlayUtils.hideOverlay(); - } - }; + FPanel panel = new FPanel(new MigLayout("insets 0, gap 0, center, wrap 3")); + panel.setOpaque(false); + panel.setBackgroundTexture(FSkin.getIcon(FSkinProp.BG_TEXTURE)); - FButton btnOk = new FButton("OK"); - btnOk.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent arg0) { - cleanup.run(); - handleOk(); - } - }); + panel.add(new FLabel.Builder().text("Choose sets").fontSize(18).build(), "center, span, wrap, gaptop 10"); - FButton btnCancel = new FButton("Cancel"); - btnCancel.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - cleanup.run(); - } - }); + String constraints = "aligny top"; + panel.add(makeCheckBoxList(coreSets, "Core sets", true), constraints); + panel.add(makeCheckBoxList(expansionSets, "Expansions", false), constraints); + panel.add(makeCheckBoxList(otherSets, "Other sets", false), constraints); - JPanel southPanel = new JPanel(new MigLayout("insets 10, gap 20, ax center")); - southPanel.setOpaque(false); - if (showWantReprintsCheckbox) { - southPanel.add(cbWantReprints, "center, span, wrap"); - } - southPanel.add(btnOk, "center, w 40%, h 20!"); - southPanel.add(btnCancel, "center, w 40%, h 20!"); + final JPanel overlay = FOverlay.SINGLETON_INSTANCE.getPanel(); + overlay.setLayout(new MigLayout("insets 0, gap 0, wrap, ax center, ay center")); - p.add(southPanel, "dock south, gapBottom 10"); + final Runnable cleanup = new Runnable() { + @Override + public void run() { + SOverlayUtils.hideOverlay(); + } + }; - overlay.add(p); - p.getRootPane().setDefaultButton(btnOk); - SOverlayUtils.showOverlay(); - } + FButton btnOk = new FButton("OK"); + btnOk.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent arg0) { + cleanup.run(); + handleOk(); + } + }); - public void setOkCallback(Runnable onOk) { - okCallback = onOk; - } + FButton btnCancel = new FButton("Cancel"); + btnCancel.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + cleanup.run(); + } + }); - // result accessors - public List getSelectedSets() { return selectedSets; } - public boolean getWantReprints() { return wantReprints; } + JPanel southPanel = new JPanel(new MigLayout("insets 10, gap 20, ax center")); + southPanel.setOpaque(false); + if (showWantReprintsCheckbox) { + southPanel.add(cbWantReprints, "center, span, wrap"); + } + southPanel.add(btnOk, "center, w 40%, h 20!"); + southPanel.add(btnCancel, "center, w 40%, h 20!"); - private JPanel makeCheckBoxList(List sets, String title, boolean focused) { - choices.addAll(sets); - final FCheckBoxList cbl = new FCheckBoxList<>(false); - cbl.setListData(sets.toArray(new FCheckBox[sets.size()])); - cbl.setVisibleRowCount(Math.min(20, sets.size())); + panel.add(southPanel, "dock south, gapBottom 10"); - if (focused) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - cbl.requestFocusInWindow(); - } - }); - } + overlay.add(panel); + panel.getRootPane().setDefaultButton(btnOk); + SOverlayUtils.showOverlay(); - JPanel pnl = new JPanel(new MigLayout("center, wrap")); - pnl.setOpaque(false); - pnl.add(new FLabel.Builder().text(title).build()); - pnl.add(new FScrollPane(cbl, true)); - return pnl; - } + } - private void handleOk() { - for (FCheckBox box : choices) { - if (box.isSelected()) { - selectedSets.add(box.getName()); - } + public void setOkCallback(Runnable onOk) { + okCallback = onOk; + } - wantReprints = cbWantReprints.isSelected(); - } + public List getSelectedSets() { + return selectedSets; + } + + public boolean getWantReprints() { + return wantReprints; + } + + private JPanel makeCheckBoxList(List sets, String title, boolean focused) { + + choices.addAll(sets); + final FCheckBoxList cbl = new FCheckBoxList<>(false); + cbl.setListData(sets.toArray(new FCheckBox[sets.size()])); + cbl.setVisibleRowCount(Math.min(20, sets.size())); + + if (focused) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + cbl.requestFocusInWindow(); + } + }); + } + + JPanel pnl = new JPanel(new MigLayout("center, wrap")); + pnl.setOpaque(false); + pnl.add(new FLabel.Builder().text(title).build()); + pnl.add(new FScrollPane(cbl, true)); + return pnl; + + } + + private void handleOk() { + + for (FCheckBox box : choices) { + if (box.isSelected()) { + selectedSets.add(box.getName()); + } + wantReprints = cbWantReprints.isSelected(); + } + + if (null != okCallback) { + okCallback.run(); + } + + } - if (null != okCallback) { - okCallback.run(); - } - } } diff --git a/forge-gui-desktop/src/main/java/forge/toolbox/FCheckBoxList.java b/forge-gui-desktop/src/main/java/forge/toolbox/FCheckBoxList.java index 59864ed27b8..57207b5c6f2 100644 --- a/forge-gui-desktop/src/main/java/forge/toolbox/FCheckBoxList.java +++ b/forge-gui-desktop/src/main/java/forge/toolbox/FCheckBoxList.java @@ -1,96 +1,153 @@ package forge.toolbox; -import java.awt.Component; -import java.awt.event.FocusEvent; -import java.awt.event.FocusListener; -import java.awt.event.KeyAdapter; -import java.awt.event.KeyEvent; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; - -import javax.swing.JList; -import javax.swing.ListCellRenderer; -import javax.swing.ListSelectionModel; -import javax.swing.UIManager; +import javax.swing.*; import javax.swing.border.Border; import javax.swing.border.EmptyBorder; +import java.awt.*; +import java.awt.event.*; /** * A list of FCheckBox items using Forge skin properties. * Call setListData() with an array of FCheckBox items to populate. - * + *

* based on code at http://www.devx.com/tips/Tip/5342 */ @SuppressWarnings("serial") public class FCheckBoxList extends JList { - protected static Border noFocusBorder = new EmptyBorder(1, 1, 1, 1); - public FCheckBoxList(final boolean keepSelectionWhenFocusLost) { - setCellRenderer(new CellRenderer()); + private static final Border NO_FOCUS_BORDER = new EmptyBorder(1, 1, 1, 1); - addMouseListener(new MouseAdapter() { - @Override - public void mousePressed(final MouseEvent e) { - final int index = locationToIndex(e.getPoint()); + private int lastClickedIndex = 0; + private int currentShiftSelectionMinIndex = Integer.MAX_VALUE; + private int currentShiftSelectionMaxIndex = -1; + private int currentHighlightMinIndex = Integer.MAX_VALUE; + private int currentHighlightMaxIndex = -1; + private boolean shiftSelectShouldCheckBox = true; - if (index != -1) { - final FCheckBox checkbox = (FCheckBox)getModel().getElementAt(index); - if (checkbox.isEnabled()) { - checkbox.setSelected(!checkbox.isSelected()); - repaint(); - } - } - } - }); + public FCheckBoxList(final boolean keepSelectionWhenFocusLost) { - addKeyListener(new KeyAdapter() { - @Override - public void keyPressed(final KeyEvent e) { - if (e.getKeyChar() == ' ') { - final FCheckBox item = (FCheckBox)getSelectedValue(); - if (null == item || !item.isEnabled()) { - return; - } + setCellRenderer(new CellRenderer()); - item.setSelected(!item.isSelected()); - repaint(); - } - } - }); + addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(final MouseEvent e) { - if (!keepSelectionWhenFocusLost) { - addFocusListener(new FocusListener() { - int lastSelectedIdx; + final int index = locationToIndex(e.getPoint()); - @Override - public void focusLost(final FocusEvent arg0) { - lastSelectedIdx = Math.max(0, getSelectedIndex()); - clearSelection(); - } + if (index != -1) { - @Override - public void focusGained(final FocusEvent arg0) { - if (-1 == getSelectedIndex()) { - setSelectedIndex(lastSelectedIdx); - } - } - }); - } + if (e.isShiftDown()) { - setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - } + int min = Math.min(lastClickedIndex, index); + int max = Math.max(lastClickedIndex, index); + + if (index == lastClickedIndex) { + currentHighlightMinIndex = lastClickedIndex; + currentHighlightMaxIndex = lastClickedIndex; + } else if (index > lastClickedIndex) { + currentHighlightMinIndex = lastClickedIndex; + currentHighlightMaxIndex = index; + } else if (index < lastClickedIndex) { + currentHighlightMinIndex = index; + currentHighlightMaxIndex = lastClickedIndex; + } + + currentShiftSelectionMinIndex = Math.min(min, currentShiftSelectionMinIndex); + currentShiftSelectionMaxIndex = Math.max(max, currentShiftSelectionMaxIndex); + + for (int i = currentShiftSelectionMinIndex; i <= currentShiftSelectionMaxIndex; i++) { + final FCheckBox checkbox = (FCheckBox) getModel().getElementAt(i); + if (shiftSelectShouldCheckBox) { + checkbox.setSelected(!shiftSelectShouldCheckBox); + } + } + + for (int i = min; i <= max; i++) { + final FCheckBox checkbox = (FCheckBox) getModel().getElementAt(i); + checkbox.setSelected(shiftSelectShouldCheckBox); + } + + } else { + + final FCheckBox checkbox = (FCheckBox) getModel().getElementAt(index); + + if (checkbox.isEnabled()) { + checkbox.setSelected(!checkbox.isSelected()); + shiftSelectShouldCheckBox = checkbox.isSelected(); + lastClickedIndex = index; + currentShiftSelectionMinIndex = currentHighlightMinIndex = Integer.MAX_VALUE; + currentShiftSelectionMaxIndex = currentHighlightMaxIndex = -1; + } + + } + + repaint(); + + } + + } + }); + + addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(final KeyEvent e) { + if (e.getKeyChar() == ' ') { + final FCheckBox item = (FCheckBox) getSelectedValue(); + if (null == item || !item.isEnabled()) { + return; + } + + item.setSelected(!item.isSelected()); + repaint(); + } + } + }); + + if (!keepSelectionWhenFocusLost) { + addFocusListener(new FocusListener() { + int lastSelectedIdx; + + @Override + public void focusLost(final FocusEvent arg0) { + lastSelectedIdx = Math.max(0, getSelectedIndex()); + clearSelection(); + } + + @Override + public void focusGained(final FocusEvent arg0) { + if (getSelectedIndex() == -1) { + setSelectedIndex(lastSelectedIdx); + } + } + }); + } + + setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + + } + + protected class CellRenderer implements ListCellRenderer { + @Override + public Component getListCellRendererComponent(final JList list, final E1 value, final int index, final boolean isSelected, final boolean cellHasFocus) { + + final FCheckBox checkbox = (FCheckBox) value; + + if (index >= currentHighlightMinIndex && index <= currentHighlightMaxIndex) { + checkbox.setOpaque(true); + } else { + checkbox.setOpaque(false); + } + + checkbox.setForeground(isSelected ? getSelectionForeground() : getForeground()); + checkbox.setBackground(getSelectionBackground()); + checkbox.setFont(getFont()); + checkbox.setFocusPainted(false); + checkbox.setBorderPainted(true); + checkbox.setBorder(isSelected ? UIManager.getBorder("List.focusCellHighlightBorder") : NO_FOCUS_BORDER); + + return checkbox; + + } + } - protected class CellRenderer implements ListCellRenderer { - @Override - public Component getListCellRendererComponent(final JList list, final E1 value, final int index, final boolean isSelected, final boolean cellHasFocus) { - final FCheckBox checkbox = (FCheckBox)value; - checkbox.setBackground(isSelected ? getSelectionBackground() : getBackground()); - checkbox.setForeground(isSelected ? getSelectionForeground() : getForeground()); - checkbox.setFont(getFont()); - checkbox.setFocusPainted(false); - checkbox.setBorderPainted(true); - checkbox.setBorder(isSelected ? UIManager.getBorder("List.focusCellHighlightBorder") : noFocusBorder); - return checkbox; - } - } }