diff --git a/forge-gui-mobile/src/forge/adventure/scene/AdventureDeckEditor.java b/forge-gui-mobile/src/forge/adventure/scene/AdventureDeckEditor.java index 0571b340f92..c9d12d0b758 100644 --- a/forge-gui-mobile/src/forge/adventure/scene/AdventureDeckEditor.java +++ b/forge-gui-mobile/src/forge/adventure/scene/AdventureDeckEditor.java @@ -85,7 +85,7 @@ public class AdventureDeckEditor extends FDeckEditor { } @Override - public ItemPool getCardPool(boolean wantUnique) { + public ItemPool getCardPool() { ItemPool pool = new ItemPool<>(PaperCard.class); pool.addAll(Current.player().getCards()); return pool; @@ -163,7 +163,7 @@ public class AdventureDeckEditor extends FDeckEditor { } @Override - public ItemPool getCardPool(boolean wantUnique) { + public ItemPool getCardPool() { return deckToPreview.getAllCardsInASinglePool(true, true); } diff --git a/forge-gui-mobile/src/forge/deck/FDeckEditor.java b/forge-gui-mobile/src/forge/deck/FDeckEditor.java index 734ed8865a6..cffda5f1bd7 100644 --- a/forge-gui-mobile/src/forge/deck/FDeckEditor.java +++ b/forge-gui-mobile/src/forge/deck/FDeckEditor.java @@ -66,8 +66,8 @@ public class FDeckEditor extends TabPageScreen { return gameType == null ? null : gameType.getDeckFormat(); } - public ItemPool getCardPool(boolean wantUnique) { - return wantUnique ? FModel.getUniqueCardsNoAlt() : FModel.getAllCardsNoAlt(); + public ItemPool getCardPool() { + return FModel.getAllCardsNoAlt(); } protected Predicate getCardFilter() { return null; } @@ -172,10 +172,10 @@ public class FDeckEditor extends TabPageScreen { @Override public boolean hasCommander() { return deckFormat.hasCommander(); } @Override - public ItemPool getCardPool(boolean wantUnique) { + public ItemPool getCardPool() { if(this.itemPoolSupplier != null) return itemPoolSupplier.get(); - return super.getCardPool(wantUnique); + return super.getCardPool(); } @Override @@ -1713,7 +1713,7 @@ public class FDeckEditor extends TabPageScreen { //Clone the pool to ensure we don't mutate it by adding to or removing from this page. //Can override this if that behavior is desired. - ItemPool cardPool = CardPool.createFrom(parentScreen.getEditorConfig().getCardPool(cardManager.getWantUnique()), PaperCard.class); + ItemPool cardPool = CardPool.createFrom(parentScreen.getEditorConfig().getCardPool(), PaperCard.class); if(editorConfig.usePlayerInventory() && currentDeck != null) { //Remove any items from the pool that are in the deck. @@ -1886,8 +1886,8 @@ public class FDeckEditor extends TabPageScreen { menu.addItem(new FCheckBoxMenuItem(Forge.getLocalizer().getMessage("lblUniqueCardsOnly"), cardManager.getWantUnique(), e -> { boolean wantUnique = !cardManager.getWantUnique(); cardManager.setWantUnique(wantUnique); - refresh(); cardManager.getConfig().setUniqueCardsOnly(wantUnique); + cardManager.refresh(); })); } } diff --git a/forge-gui-mobile/src/forge/deck/FDeckImportDialog.java b/forge-gui-mobile/src/forge/deck/FDeckImportDialog.java index 9202258fbb9..7462d06304c 100644 --- a/forge-gui-mobile/src/forge/deck/FDeckImportDialog.java +++ b/forge-gui-mobile/src/forge/deck/FDeckImportDialog.java @@ -76,7 +76,7 @@ public class FDeckImportDialog extends FDialog { boolean replacingDeck = !currentDeck.isEmpty() || usingInventory; this.currentDeck = currentDeck; this.editorConfig = editorConfig; - ItemPool cardPool = editorConfig.getCardPool(false); + ItemPool cardPool = editorConfig.getCardPool(); controller = new DeckImportController(dateTimeCheck, monthDropdown, yearDropdown, replacingDeck); String contents = Forge.getClipboard().getContents(); if (contents == null) diff --git a/forge-gui-mobile/src/forge/itemmanager/CardManager.java b/forge-gui-mobile/src/forge/itemmanager/CardManager.java index 3827cd4471b..a310bfc9440 100644 --- a/forge-gui-mobile/src/forge/itemmanager/CardManager.java +++ b/forge-gui-mobile/src/forge/itemmanager/CardManager.java @@ -1,10 +1,18 @@ package forge.itemmanager; +import java.util.*; import java.util.Map.Entry; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import com.google.common.collect.ListMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Multimaps; import forge.Graphics; +import forge.StaticData; import forge.assets.FSkinColor; import forge.assets.FSkinFont; +import forge.card.CardEdition; import forge.card.CardRenderer; import forge.card.CardZoom; import forge.item.PaperCard; @@ -60,6 +68,77 @@ public class CardManager extends ItemManager { return new AdvancedSearchFilter<>(itemManager); } + @Override + protected Iterable> getUnique(Iterable> items) { + //TO-maybe-DO: Share logic between this and identical method in desktop. + ListMultimap> entriesByName = Multimaps.newListMultimap( + new TreeMap<>(String.CASE_INSENSITIVE_ORDER), Lists::newArrayList); + for (Entry item : items) { + final String cardName = item.getKey().getName(); + entriesByName.put(cardName, item); + } + + // Now we're ready to go on with retrieving cards to be returned + Map cardsMap = new HashMap<>(); + for (String cardName : entriesByName.keySet()) { + List> entries = entriesByName.get(cardName); + + ListMultimap> entriesByEdition = Multimaps.newListMultimap(new HashMap<>(), Lists::newArrayList); + for (Entry entry : entries) { + CardEdition ed = StaticData.instance().getCardEdition(entry.getKey().getEdition()); + if (ed != null) + entriesByEdition.put(ed, entry); + } + if (entriesByEdition.isEmpty()) + continue; // skip card + + // Try to retain only those editions accepted by the current Card Art Preference Policy + Predicate editionPredicate = ed -> StaticData.instance().getCardArtPreference().accept(ed); + List acceptedEditions = entriesByEdition.keySet().stream().filter(editionPredicate).collect(Collectors.toList()); + + // If policy too strict, fall back to getting all editions. + if (acceptedEditions.isEmpty()) + // Policy is too strict for current PaperCard in Entry. Remove any filter + acceptedEditions.addAll(entriesByEdition.keySet()); + + Entry cardEntry = getCardEntryToAdd(entriesByEdition, acceptedEditions); + if (cardEntry != null) + cardsMap.put(cardEntry.getKey(), cardEntry.getValue()); + } + return cardsMap.entrySet(); + } + + // Select the Card Art Entry to add, based on current Card Art Preference Order. + // This method will prefer the entry currently having an image. If that's not the case, + private Entry getCardEntryToAdd(ListMultimap> entriesByEdition, + List acceptedEditions) { + // Use standard sort + index, for better performance! + Collections.sort(acceptedEditions); + if (StaticData.instance().cardArtPreferenceIsLatest()) + Collections.reverse(acceptedEditions); + Iterator editionIterator = acceptedEditions.iterator(); + Entry candidateEntry = null; + Entry firstCandidateEntryFound = null; + while (editionIterator.hasNext() && candidateEntry == null){ + CardEdition cardEdition = editionIterator.next(); + // These are now the entries to add to Cards Map + List> cardEntries = entriesByEdition.get(cardEdition); + Iterator> entriesIterator = cardEntries.iterator(); + candidateEntry = entriesIterator.hasNext() ? entriesIterator.next() : null; + if (candidateEntry != null && firstCandidateEntryFound == null) + firstCandidateEntryFound = candidateEntry; // save reference to the first candidate entry found! + while ((candidateEntry == null || !candidateEntry.getKey().hasImage()) && entriesIterator.hasNext()) { + candidateEntry = entriesIterator.next(); + if (firstCandidateEntryFound == null) + firstCandidateEntryFound = candidateEntry; + } + + if (candidateEntry != null && !candidateEntry.getKey().hasImage()) + candidateEntry = null; // resetting for next edition + } + return candidateEntry != null ? candidateEntry : firstCandidateEntryFound; + } + @Override public ItemRenderer getListItemRenderer(final CompactModeHandler compactModeHandler) { return new CardListItemRenderer(compactModeHandler); diff --git a/forge-gui-mobile/src/forge/itemmanager/ItemManager.java b/forge-gui-mobile/src/forge/itemmanager/ItemManager.java index 3b548f5cf06..f2ed5fee7ad 100644 --- a/forge-gui-mobile/src/forge/itemmanager/ItemManager.java +++ b/forge-gui-mobile/src/forge/itemmanager/ItemManager.java @@ -728,6 +728,10 @@ public abstract class ItemManager extends FContainer im protected abstract AdvancedSearchFilter createAdvancedSearchFilter(); + protected Iterable> getUnique(final Iterable> items) { + return Aggregates.uniqueByLast(items, from -> from.getKey().getName()); + } + public void addFilter(final ItemFilter filter) { filters.get().add(filter); add(filter.getWidget()); @@ -908,19 +912,29 @@ public abstract class ItemManager extends FContainer im } public void updateView(final boolean forceFilter, final Iterable itemsToSelect) { + //TO-maybe-DO: Share logic between this and identical method in desktop. final boolean useFilter = (forceFilter && (filterPredicate != null)) || !isUnfiltered(); - if (useFilter || forceFilter) { - model.clear(); - - Iterable> items = pool; - if (useFilter) { - Predicate> pred = x -> x != null && filterPredicate.test(x.getKey()); - items = IterableUtil.filter(pool, pred); - } - model.addItems(items); + if (useFilter || this.wantUnique || forceFilter) { + this.model.clear(); } + if (useFilter && this.wantUnique) { + final Predicate> filterForPool = x -> this.filterPredicate.test(x.getKey()); + final Iterable> items = getUnique(IterableUtil.filter(this.pool, filterForPool)); + this.model.addItems(items); + } + else if (useFilter) { + final Predicate> pred = x -> this.filterPredicate.test(x.getKey()); + this.model.addItems(IterableUtil.filter(this.pool, pred)); + } + else if (this.wantUnique) { + final Iterable> items = getUnique(this.pool); + this.model.addItems(items); + } + else if (forceFilter) { + this.model.addItems(this.pool); + } currentView.refresh(itemsToSelect, getSelectedIndex(), forceFilter ? 0 : currentView.getScrollValue()); //update ratio of # in filtered pool / # in total pool