mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-19 20:28:00 +00:00
Merge remote-tracking branch 'remotes/core/master' into newBranch
This commit is contained in:
@@ -12,10 +12,12 @@ public class GameEventSpellAbilityCast extends GameEvent {
|
||||
public final SpellAbility sa;
|
||||
public final SpellAbilityStackInstance si;
|
||||
public final boolean replicate;
|
||||
public final int stackIndex;
|
||||
|
||||
public GameEventSpellAbilityCast(SpellAbility sp, SpellAbilityStackInstance si, boolean replicate) {
|
||||
public GameEventSpellAbilityCast(SpellAbility sp, SpellAbilityStackInstance si, int stackIndex, boolean replicate) {
|
||||
sa = sp;
|
||||
this.si = si;
|
||||
this.stackIndex = stackIndex;
|
||||
this.replicate = replicate;
|
||||
}
|
||||
|
||||
|
||||
@@ -414,6 +414,7 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
|
||||
final SpellAbilityStackInstance si = new SpellAbilityStackInstance(sp);
|
||||
|
||||
stack.addFirst(si);
|
||||
int stackIndex = stack.size() - 1;
|
||||
|
||||
// 2012-07-21 the following comparison needs to move below the pushes but somehow screws up priority
|
||||
// When it's down there. That makes absolutely no sense to me, so i'm putting it back for now
|
||||
@@ -430,7 +431,7 @@ public class MagicStack /* extends MyObservable */ implements Iterable<SpellAbil
|
||||
sp.getActivatingPlayer().setActivateLoyaltyAbilityThisTurn(true);
|
||||
}
|
||||
game.updateStackForView();
|
||||
game.fireEvent(new GameEventSpellAbilityCast(sp, si, false));
|
||||
game.fireEvent(new GameEventSpellAbilityCast(sp, si, stackIndex, false));
|
||||
return si;
|
||||
}
|
||||
|
||||
|
||||
@@ -288,16 +288,17 @@ public class GuiChoose {
|
||||
public static List<CardView> manipulateCardList(final CMatchUI gui, final String title, final Iterable<CardView> cards, final Iterable<CardView> manipulable,
|
||||
final boolean toTop, final boolean toBottom, final boolean toAnywhere) {
|
||||
gui.setSelectables(manipulable);
|
||||
final Callable<List<CardView>> callable = new Callable<List<CardView>>() {
|
||||
@Override
|
||||
public List<CardView> call() {
|
||||
ListCardArea tempArea = ListCardArea.show(gui,title,cards,manipulable,toTop,toBottom,toAnywhere);
|
||||
// tempArea.pack();
|
||||
tempArea.show();
|
||||
final List<CardView> cardList = tempArea.getCards();
|
||||
return cardList;
|
||||
}
|
||||
};
|
||||
@SuppressWarnings("Convert2Lambda") // Avoid lambdas to maintain compatibility with Android 5 API
|
||||
final Callable<List<CardView>> callable = new Callable<List<CardView>>() {
|
||||
@Override
|
||||
public List<CardView> call() {
|
||||
ListCardArea tempArea = ListCardArea.show(gui,title,cards,manipulable,toTop,toBottom,toAnywhere);
|
||||
|
||||
// tempArea.pack();
|
||||
tempArea.setVisible(true);
|
||||
return tempArea.getCards();
|
||||
}
|
||||
};
|
||||
final FutureTask<List<CardView>> ft = new FutureTask<>(callable);
|
||||
FThreads.invokeInEdtAndWait(ft);
|
||||
gui.clearSelectables();
|
||||
|
||||
@@ -17,10 +17,9 @@ import forge.screens.home.EMenuGroup;
|
||||
import forge.screens.home.IVSubmenu;
|
||||
import forge.screens.home.VHomeUI;
|
||||
import forge.toolbox.*;
|
||||
import forge.util.storage.IStorage;
|
||||
import forge.util.Localizer;
|
||||
import forge.util.WordUtil;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import org.apache.commons.lang3.text.WordUtils;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.plaf.basic.BasicComboBoxRenderer;
|
||||
@@ -243,17 +242,15 @@ public enum VSubmenuQuestData implements IVSubmenu<CSubmenuQuestData> {
|
||||
cboAllowUnlocks.setSelected(true);
|
||||
|
||||
final Map<String, String> preconDescriptions = new HashMap<>();
|
||||
final IStorage<PreconDeck> preconDecks = QuestController.getPrecons();
|
||||
|
||||
for (final PreconDeck preconDeck : preconDecks) {
|
||||
for (final PreconDeck preconDeck : QuestController.getPrecons()) {
|
||||
if (QuestController.getPreconDeals(preconDeck).getMinWins() > 0) {
|
||||
continue;
|
||||
}
|
||||
final String name = preconDeck.getName();
|
||||
cbxPreconDeck.addItem(name);
|
||||
String description = preconDeck.getDescription();
|
||||
description = "<html>" + WordUtils.wrap(description, 40, "<br>", false) + "</html>";
|
||||
preconDescriptions.put(name, description);
|
||||
preconDescriptions.put(name, WordUtil.wordWrapAsHTML(description));
|
||||
}
|
||||
|
||||
// The cbx needs strictly typed renderer
|
||||
|
||||
@@ -227,6 +227,7 @@ public enum CSubmenuPreferences implements ICDoc {
|
||||
initializeDefaultFontSizeComboBox();
|
||||
initializeMulliganRuleComboBox();
|
||||
initializeAiProfilesComboBox();
|
||||
initializeStackAdditionsComboBox();
|
||||
initializeColorIdentityCombobox();
|
||||
initializeAutoYieldModeComboBox();
|
||||
initializeCounterDisplayTypeComboBox();
|
||||
@@ -408,6 +409,16 @@ public enum CSubmenuPreferences implements ICDoc {
|
||||
panel.setComboBox(comboBox, selectedItem);
|
||||
}
|
||||
|
||||
private void initializeStackAdditionsComboBox() {
|
||||
final String[] elems = {ForgeConstants.STACK_EFFECT_NOTIFICATION_NEVER, ForgeConstants.STACK_EFFECT_NOTIFICATION_ALWAYS,
|
||||
ForgeConstants.STACK_EFFECT_NOTIFICATION_AI_AND_TRIGGERED};
|
||||
final FPref userSetting = FPref.UI_STACK_EFFECT_NOTIFICATION_POLICY;
|
||||
final FComboBoxPanel<String> panel = this.view.getCbpStackAdditionsComboBoxPanel();
|
||||
final FComboBox<String> comboBox = createComboBox(elems, userSetting);
|
||||
final String selectedItem = this.prefs.getPref(userSetting);
|
||||
panel.setComboBox(comboBox, selectedItem);
|
||||
}
|
||||
|
||||
private void initializeColorIdentityCombobox() {
|
||||
final String[] elems = {ForgeConstants.DISP_CURRENT_COLORS_NEVER, ForgeConstants.DISP_CURRENT_COLORS_CHANGED,
|
||||
ForgeConstants.DISP_CURRENT_COLORS_MULTICOLOR, ForgeConstants.DISP_CURRENT_COLORS_MULTI_OR_CHANGED,
|
||||
|
||||
@@ -116,6 +116,7 @@ public enum VSubmenuPreferences implements IVSubmenu<CSubmenuPreferences> {
|
||||
private final FComboBoxPanel<String> cbpDefaultFontSize = new FComboBoxPanel<>(localizer.getMessage("cbpDefaultFontSize")+":");
|
||||
private final FComboBoxPanel<String> cbpMulliganRule = new FComboBoxPanel<>(localizer.getMessage("cbpMulliganRule")+":");
|
||||
private final FComboBoxPanel<String> cbpAiProfiles = new FComboBoxPanel<>(localizer.getMessage("cbpAiProfiles")+":");
|
||||
private final FComboBoxPanel<String> cbpStackAdditions = new FComboBoxPanel<>(localizer.getMessage("cbpStackAdditions")+":");
|
||||
private final FComboBoxPanel<String> cbpDisplayCurrentCardColors = new FComboBoxPanel<>(localizer.getMessage("cbpDisplayCurrentCardColors")+":");
|
||||
private final FComboBoxPanel<String> cbpAutoYieldMode = new FComboBoxPanel<>(localizer.getMessage("cbpAutoYieldMode")+":");
|
||||
private final FComboBoxPanel<String> cbpCounterDisplayType = new FComboBoxPanel<>(localizer.getMessage("cbpCounterDisplayType")+":");
|
||||
@@ -194,6 +195,9 @@ public enum VSubmenuPreferences implements IVSubmenu<CSubmenuPreferences> {
|
||||
pnlPrefs.add(cbManaLostPrompt, titleConstraints);
|
||||
pnlPrefs.add(new NoteLabel(localizer.getMessage("nlManaLostPrompt")), descriptionConstraints);
|
||||
|
||||
pnlPrefs.add(cbpStackAdditions, comboBoxConstraints);
|
||||
pnlPrefs.add(new NoteLabel(localizer.getMessage("nlpStackAdditions")), descriptionConstraints);
|
||||
|
||||
pnlPrefs.add(cbEnforceDeckLegality, titleConstraints);
|
||||
pnlPrefs.add(new NoteLabel(localizer.getMessage("nlEnforceDeckLegality")), descriptionConstraints);
|
||||
|
||||
@@ -649,6 +653,10 @@ public enum VSubmenuPreferences implements IVSubmenu<CSubmenuPreferences> {
|
||||
return cbpAiProfiles;
|
||||
}
|
||||
|
||||
public FComboBoxPanel<String> getCbpStackAdditionsComboBoxPanel() {
|
||||
return cbpStackAdditions;
|
||||
}
|
||||
|
||||
public FComboBoxPanel<GameLogEntryType> getGameLogVerbosityComboBoxPanel() {
|
||||
return cbpGameLogEntryType;
|
||||
}
|
||||
@@ -766,7 +774,7 @@ public enum VSubmenuPreferences implements IVSubmenu<CSubmenuPreferences> {
|
||||
public final JCheckBox getCbManaLostPrompt() {
|
||||
return cbManaLostPrompt;
|
||||
}
|
||||
|
||||
|
||||
public final JCheckBox getCbDetailedPaymentDesc() {
|
||||
return cbDetailedPaymentDesc;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,9 @@
|
||||
package forge.screens.match;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@@ -30,6 +32,8 @@ import java.util.Map.Entry;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import javax.swing.JMenu;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.KeyStroke;
|
||||
import javax.swing.SwingUtilities;
|
||||
@@ -41,25 +45,43 @@ import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import forge.FThreads;
|
||||
import forge.GuiBase;
|
||||
import forge.ImageCache;
|
||||
import forge.LobbyPlayer;
|
||||
import forge.Singletons;
|
||||
import forge.StaticData;
|
||||
import forge.assets.FSkinProp;
|
||||
import forge.card.CardStateName;
|
||||
import forge.control.KeyboardShortcuts;
|
||||
import forge.deck.CardPool;
|
||||
import forge.deck.Deck;
|
||||
import forge.deckchooser.FDeckViewer;
|
||||
import forge.game.GameEntity;
|
||||
import forge.game.GameEntityView;
|
||||
import forge.game.GameView;
|
||||
import forge.game.ability.AbilityKey;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardView;
|
||||
import forge.game.card.CardView.CardStateView;
|
||||
import forge.game.combat.CombatView;
|
||||
import forge.game.event.GameEventSpellAbilityCast;
|
||||
import forge.game.event.GameEventSpellRemovedFromStack;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.DelayedReveal;
|
||||
import forge.game.player.IHasIcon;
|
||||
import forge.game.player.PlayerView;
|
||||
import forge.game.spellability.SpellAbility;
|
||||
import forge.game.spellability.SpellAbilityStackInstance;
|
||||
import forge.game.spellability.SpellAbilityView;
|
||||
import forge.game.spellability.StackItemView;
|
||||
import forge.game.spellability.TargetChoices;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.gui.*;
|
||||
import forge.gui.FNetOverlay;
|
||||
import forge.gui.GuiChoose;
|
||||
import forge.gui.GuiDialog;
|
||||
import forge.gui.GuiUtils;
|
||||
import forge.gui.SOverlayUtils;
|
||||
import forge.gui.framework.DragCell;
|
||||
import forge.gui.framework.EDocID;
|
||||
import forge.gui.framework.FScreen;
|
||||
@@ -74,6 +96,7 @@ import forge.match.AbstractGuiGame;
|
||||
import forge.menus.IMenuProvider;
|
||||
import forge.model.FModel;
|
||||
import forge.player.PlayerZoneUpdate;
|
||||
import forge.properties.ForgeConstants;
|
||||
import forge.properties.ForgePreferences;
|
||||
import forge.properties.ForgePreferences.FPref;
|
||||
import forge.screens.match.controllers.CAntes;
|
||||
@@ -88,19 +111,25 @@ import forge.screens.match.menus.CMatchUIMenus;
|
||||
import forge.screens.match.views.VField;
|
||||
import forge.screens.match.views.VHand;
|
||||
import forge.toolbox.FButton;
|
||||
import forge.toolbox.FLabel;
|
||||
import forge.toolbox.FOptionPane;
|
||||
import forge.toolbox.FSkin;
|
||||
import forge.toolbox.FSkin.SkinImage;
|
||||
import forge.toolbox.FTextArea;
|
||||
import forge.toolbox.imaging.FImagePanel;
|
||||
import forge.toolbox.imaging.FImagePanel.AutoSizeImageMode;
|
||||
import forge.toolbox.imaging.FImageUtil;
|
||||
import forge.toolbox.special.PhaseIndicator;
|
||||
import forge.toolbox.special.PhaseLabel;
|
||||
import forge.trackable.TrackableCollection;
|
||||
import forge.util.ITriggerEvent;
|
||||
import forge.util.collect.FCollection;
|
||||
import forge.util.collect.FCollectionView;
|
||||
import forge.util.ITriggerEvent;
|
||||
import forge.util.gui.SOptionPane;
|
||||
import forge.view.FView;
|
||||
import forge.view.arcane.CardPanel;
|
||||
import forge.view.arcane.FloatingZone;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
/**
|
||||
* Constructs instance of match UI controller, used as a single point of
|
||||
@@ -136,6 +165,7 @@ public final class CMatchUI
|
||||
private final CLog cLog = new CLog(this);
|
||||
private final CPrompt cPrompt = new CPrompt(this);
|
||||
private final CStack cStack = new CStack(this);
|
||||
private int nextNotifiableStackIndex = 0;
|
||||
|
||||
public CMatchUI() {
|
||||
this.view = new VMatchUI(this);
|
||||
@@ -1180,4 +1210,204 @@ public final class CMatchUI
|
||||
return reply == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyStackAddition(GameEventSpellAbilityCast event) {
|
||||
SpellAbility sa = event.sa;
|
||||
String stackNotificationPolicy = FModel.getPreferences().getPref(FPref.UI_STACK_EFFECT_NOTIFICATION_POLICY);
|
||||
boolean isAi = sa.getActivatingPlayer().isAI();
|
||||
boolean isTrigger = sa.isTrigger();
|
||||
int stackIndex = event.stackIndex;
|
||||
if(stackIndex == nextNotifiableStackIndex) {
|
||||
if(ForgeConstants.STACK_EFFECT_NOTIFICATION_ALWAYS.equals(stackNotificationPolicy) || (ForgeConstants.STACK_EFFECT_NOTIFICATION_AI_AND_TRIGGERED.equals(stackNotificationPolicy) && (isAi || isTrigger))) {
|
||||
// We can go and show the modal
|
||||
SpellAbilityStackInstance si = event.si;
|
||||
|
||||
MigLayout migLayout = new MigLayout("insets 15, left, gap 30, fill");
|
||||
JPanel mainPanel = new JPanel(migLayout);
|
||||
final Dimension parentSize = JOptionPane.getRootFrame().getSize();
|
||||
Dimension maxSize = new Dimension(1400, parentSize.height - 100);
|
||||
mainPanel.setMaximumSize(maxSize);
|
||||
mainPanel.setOpaque(false);
|
||||
|
||||
// Big Image
|
||||
addBigImageToStackModalPanel(mainPanel, si);
|
||||
|
||||
// Text
|
||||
addTextToStackModalPanel(mainPanel,sa,si);
|
||||
|
||||
// Small images
|
||||
int numSmallImages = 0;
|
||||
|
||||
// If current effect is a triggered/activated ability of an enchantment card, I want to show the enchanted card
|
||||
GameEntityView enchantedEntityView = null;
|
||||
Card hostCard = sa.getHostCard();
|
||||
if(hostCard.isEnchantment()) {
|
||||
GameEntity enchantedEntity = hostCard.getEntityAttachedTo();
|
||||
if(enchantedEntity != null) {
|
||||
enchantedEntityView = enchantedEntity.getView();
|
||||
numSmallImages++;
|
||||
} else if ((sa.getRootAbility() != null)
|
||||
&& (sa.getRootAbility().getPaidList("Sacrificed") != null)
|
||||
&& !sa.getRootAbility().getPaidList("Sacrificed").isEmpty()) {
|
||||
// If the player activated its ability by sacrificing the enchantment, the enchantment has not anything attached anymore and the ex-enchanted card has to be searched in other ways.. for example, the green enchantment "Carapace"
|
||||
enchantedEntity = sa.getRootAbility().getPaidList("Sacrificed").get(0).getEnchantingCard();
|
||||
if(enchantedEntity != null) {
|
||||
enchantedEntityView = enchantedEntity.getView();
|
||||
numSmallImages++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If current effect is a triggered ability, I want to show the triggering card if present
|
||||
SpellAbility sourceSA = (SpellAbility) si.getTriggeringObject(AbilityKey.SourceSA);
|
||||
CardView sourceCardView = null;
|
||||
if(sourceSA != null) {
|
||||
sourceCardView = sourceSA.getHostCard().getView();
|
||||
numSmallImages++;
|
||||
}
|
||||
|
||||
// I also want to show each type of targets (both cards and players)
|
||||
List<GameEntityView> targets = getTargets(si,new ArrayList<GameEntityView>());
|
||||
numSmallImages = numSmallImages + targets.size();
|
||||
|
||||
// Now I know how many small images - on to render them
|
||||
if(enchantedEntityView != null) {
|
||||
addSmallImageToStackModalPanel(enchantedEntityView,mainPanel,numSmallImages);
|
||||
}
|
||||
if(sourceCardView != null) {
|
||||
addSmallImageToStackModalPanel(sourceCardView,mainPanel,numSmallImages);
|
||||
}
|
||||
for(GameEntityView gev : targets) {
|
||||
addSmallImageToStackModalPanel(gev, mainPanel, numSmallImages);
|
||||
}
|
||||
|
||||
FOptionPane.showOptionDialog(null, "Forge", null, mainPanel, ImmutableList.of("OK"));
|
||||
// here the user closed the modal - time to update the next notifiable stack index
|
||||
|
||||
}
|
||||
// In any case, I have to increase the counter
|
||||
nextNotifiableStackIndex++;
|
||||
|
||||
} else {
|
||||
|
||||
// Not yet time to show the modal - schedule the method again, and try again later
|
||||
Runnable tryAgainThread = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
notifyStackAddition(event);
|
||||
}
|
||||
};
|
||||
GuiBase.getInterface().invokeInEdtLater(tryAgainThread);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private List<GameEntityView> getTargets(SpellAbilityStackInstance si, List<GameEntityView> result){
|
||||
if(si == null) {
|
||||
return result;
|
||||
} else {
|
||||
FCollectionView<CardView> targetCards = CardView.getCollection(si.getTargetChoices().getTargetCards());
|
||||
for(CardView currCardView: targetCards) {
|
||||
result.add(currCardView);
|
||||
}
|
||||
|
||||
for(SpellAbility currSA : si.getTargetChoices().getTargetSpells()) {
|
||||
CardView currCardView = currSA.getCardView();
|
||||
result.add(currCardView);
|
||||
}
|
||||
|
||||
FCollectionView<PlayerView> targetPlayers = PlayerView.getCollection(si.getTargetChoices().getTargetPlayers());
|
||||
for(PlayerView currPlayerView : targetPlayers) {
|
||||
result.add(currPlayerView);
|
||||
}
|
||||
|
||||
return getTargets(si.getSubInstance(),result);
|
||||
}
|
||||
}
|
||||
|
||||
private void addBigImageToStackModalPanel(JPanel mainPanel, SpellAbilityStackInstance si) {
|
||||
StackItemView siv = si.getView();
|
||||
int rotation = getRotation(si.getCardView());
|
||||
|
||||
FImagePanel imagePanel = new FImagePanel();
|
||||
BufferedImage bufferedImage = FImageUtil.getImage(siv.getSourceCard().getCurrentState());
|
||||
imagePanel.setImage(bufferedImage, rotation, AutoSizeImageMode.SOURCE);
|
||||
int imageWidth = 433;
|
||||
int imageHeight = 600;
|
||||
Dimension imagePanelDimension = new Dimension(imageWidth,imageHeight);
|
||||
imagePanel.setMinimumSize(imagePanelDimension);
|
||||
|
||||
mainPanel.add(imagePanel, "cell 0 0, spany 3");
|
||||
}
|
||||
|
||||
private void addTextToStackModalPanel(JPanel mainPanel, SpellAbility sa, SpellAbilityStackInstance si) {
|
||||
String who = sa.getActivatingPlayer().getName();
|
||||
String action = sa.isSpell() ? " cast " : sa.isTrigger() ? " triggered " : " activated ";
|
||||
String what = sa.getStackDescription().startsWith("Morph ") ? "Morph" : sa.getHostCard().toString();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(who).append(action).append(what);
|
||||
|
||||
if (sa.getTargetRestrictions() != null) {
|
||||
sb.append(" targeting ");
|
||||
TargetChoices targets = si.getTargetChoices();
|
||||
sb.append(targets.getTargetedString());
|
||||
}
|
||||
sb.append(".");
|
||||
String message1 = sb.toString();
|
||||
String message2 = si.getStackDescription();
|
||||
String messageTotal = message1 + "\n\n" + message2;
|
||||
|
||||
final FTextArea prompt1 = new FTextArea(messageTotal);
|
||||
prompt1.setFont(FSkin.getFont(21));
|
||||
prompt1.setAutoSize(true);
|
||||
prompt1.setMinimumSize(new Dimension(475,200));
|
||||
mainPanel.add(prompt1, "cell 1 0, aligny top");
|
||||
}
|
||||
|
||||
private void addSmallImageToStackModalPanel(GameEntityView gameEntityView, JPanel mainPanel, int numTarget) {
|
||||
if(gameEntityView instanceof CardView) {
|
||||
CardView cardView = (CardView) gameEntityView;
|
||||
int currRotation = getRotation(cardView);
|
||||
FImagePanel targetPanel = new FImagePanel();
|
||||
BufferedImage bufferedImage = FImageUtil.getImage(cardView.getCurrentState());
|
||||
targetPanel.setImage(bufferedImage, currRotation, AutoSizeImageMode.SOURCE);
|
||||
int imageWidth = 217;
|
||||
int imageHeight = 300;
|
||||
Dimension targetPanelDimension = new Dimension(imageWidth,imageHeight);
|
||||
targetPanel.setMinimumSize(targetPanelDimension);
|
||||
mainPanel.add(targetPanel, "cell 1 1, split " + numTarget+ ", aligny bottom");
|
||||
} else if(gameEntityView instanceof PlayerView) {
|
||||
PlayerView playerView = (PlayerView) gameEntityView;
|
||||
SkinImage playerAvatar = getPlayerAvatar(playerView, 0);
|
||||
final FLabel lblIcon = new FLabel.Builder().icon(playerAvatar).build();
|
||||
Dimension dimension = playerAvatar.getSizeForPaint(JOptionPane.getRootFrame().getGraphics());
|
||||
mainPanel.add(lblIcon, "cell 1 1, split " + numTarget+ ", w " + dimension.getWidth() + ", h " + dimension.getHeight() + ", aligny bottom");
|
||||
}
|
||||
}
|
||||
|
||||
private int getRotation(CardView cardView) {
|
||||
final int rotation;
|
||||
if (cardView.isSplitCard()) {
|
||||
String cardName = cardView.getName();
|
||||
if (cardName.isEmpty()) { cardName = cardView.getAlternateState().getName(); }
|
||||
|
||||
PaperCard pc = StaticData.instance().getCommonCards().getCard(cardName);
|
||||
boolean hasKeywordAftermath = pc != null && Card.getCardForUi(pc).hasKeyword(Keyword.AFTERMATH);
|
||||
|
||||
rotation = cardView.isFaceDown() ? 0 : hasKeywordAftermath ? (CardStateName.LeftSplit.equals(cardView.getState(false).getState()) ? 0 : 270) : 90; // rotate Aftermath splits the other way to correctly show the right split (graveyard) half
|
||||
} else {
|
||||
CardStateView cardStateView = cardView.getState(false);
|
||||
rotation = cardStateView.isPlane() || cardStateView.isPhenomenon() ? 90 : 0;
|
||||
}
|
||||
|
||||
return rotation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyStackRemoval(GameEventSpellRemovedFromStack event) {
|
||||
// I always decrease the counter
|
||||
nextNotifiableStackIndex--;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -78,6 +78,10 @@ public class FOptionPane extends FDialog {
|
||||
return showOptionDialog(message, title, icon, options, 0);
|
||||
}
|
||||
|
||||
public static int showOptionDialog(final String message, final String title, final SkinImage icon, Component comp, final List<String> options) {
|
||||
return showOptionDialog(message, title, icon, comp, options, 0);
|
||||
}
|
||||
|
||||
public static int showOptionDialog(final String message, final String title, final SkinImage icon, final List<String> options, final int defaultOption) {
|
||||
final FOptionPane optionPane = new FOptionPane(message, title, icon, null, options, defaultOption);
|
||||
optionPane.setVisible(true);
|
||||
@@ -86,6 +90,14 @@ public class FOptionPane extends FDialog {
|
||||
return dialogResult;
|
||||
}
|
||||
|
||||
public static int showOptionDialog(final String message, final String title, final SkinImage icon, final Component comp, final List<String> options, final int defaultOption) {
|
||||
final FOptionPane optionPane = new FOptionPane(message, title, icon, comp, options, defaultOption);
|
||||
optionPane.setVisible(true);
|
||||
final int dialogResult = optionPane.result;
|
||||
optionPane.dispose();
|
||||
return dialogResult;
|
||||
}
|
||||
|
||||
public static String showInputDialog(final String message, final String title) {
|
||||
return showInputDialog(message, title, null, "", null);
|
||||
}
|
||||
|
||||
@@ -28,8 +28,8 @@ import forge.properties.ForgeConstants;
|
||||
import forge.properties.ForgePreferences;
|
||||
import forge.properties.ForgePreferences.FPref;
|
||||
import forge.util.OperatingSystem;
|
||||
import forge.util.WordUtil;
|
||||
import forge.view.FView;
|
||||
import org.apache.commons.lang3.text.WordUtils;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.swing.*;
|
||||
@@ -1085,7 +1085,7 @@ public class FSkin {
|
||||
allSkins = new ArrayList<>();
|
||||
final List<String> skinDirectoryNames = getSkinDirectoryNames();
|
||||
for (String skinDirectoryName : skinDirectoryNames) {
|
||||
allSkins.add(WordUtils.capitalize(skinDirectoryName.replace('_', ' ')));
|
||||
allSkins.add(WordUtil.capitalize(skinDirectoryName.replace('_', ' ')));
|
||||
}
|
||||
Collections.sort(allSkins);
|
||||
}
|
||||
|
||||
@@ -1,31 +1,24 @@
|
||||
package forge.toolbox.special;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Font;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.SwingConstants;
|
||||
|
||||
import forge.card.mana.ManaAtom;
|
||||
import forge.trackable.TrackableProperty;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
|
||||
import forge.assets.FSkinProp;
|
||||
import forge.card.mana.ManaAtom;
|
||||
import forge.game.player.PlayerView;
|
||||
import forge.toolbox.FLabel;
|
||||
import forge.toolbox.FMouseAdapter;
|
||||
import forge.toolbox.FSkin;
|
||||
import forge.toolbox.FSkin.SkinFont;
|
||||
import forge.toolbox.FSkin.SkinnedPanel;
|
||||
import forge.trackable.TrackableProperty;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class PlayerDetailsPanel extends JPanel {
|
||||
private static final long serialVersionUID = -6531759554646891983L;
|
||||
|
||||
@@ -12,8 +12,8 @@ import forge.game.*;
|
||||
import forge.properties.ForgeConstants;
|
||||
import forge.tournament.system.*;
|
||||
import forge.util.TextUtil;
|
||||
import forge.util.WordUtil;
|
||||
import forge.util.storage.IStorage;
|
||||
import org.apache.commons.lang3.text.WordUtils;
|
||||
import org.apache.commons.lang3.time.StopWatch;
|
||||
|
||||
import forge.deck.Deck;
|
||||
@@ -75,7 +75,7 @@ public class SimulateMatch {
|
||||
|
||||
GameType type = GameType.Constructed;
|
||||
if (params.containsKey("f")) {
|
||||
type = GameType.valueOf(WordUtils.capitalize(params.get("f").get(0)));
|
||||
type = GameType.valueOf(WordUtil.capitalize(params.get("f").get(0)));
|
||||
}
|
||||
|
||||
GameRules rules = new GameRules(type);
|
||||
|
||||
@@ -687,7 +687,7 @@ public class GifDecoder {
|
||||
} while ((blockSize > 0) && !err());
|
||||
}
|
||||
|
||||
public Animation getAnimation(PlayMode playType) {
|
||||
private Animation<TextureRegion> getAnimation(PlayMode playType) {
|
||||
int nrFrames = getFrameCount();
|
||||
Pixmap frame = getFrame(0);
|
||||
int width = frame.getWidth();
|
||||
@@ -725,12 +725,11 @@ public class GifDecoder {
|
||||
}
|
||||
float frameDuration = (float)getDelay(0);
|
||||
frameDuration /= 1000; // convert milliseconds into seconds
|
||||
Animation result = new Animation(frameDuration, texReg, playType);
|
||||
|
||||
return result;
|
||||
return new Animation<>(frameDuration, texReg, playType);
|
||||
}
|
||||
|
||||
public static Animation loadGIFAnimation(PlayMode playType, InputStream is) {
|
||||
public static Animation<TextureRegion> loadGIFAnimation(PlayMode playType, InputStream is) {
|
||||
GifDecoder gdec = new GifDecoder();
|
||||
gdec.read(is);
|
||||
return gdec.getAnimation(playType);
|
||||
|
||||
@@ -3,9 +3,9 @@ package forge.assets;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import forge.util.WordUtil;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import forge.Forge;
|
||||
import org.apache.commons.lang3.text.WordUtils;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.files.FileHandle;
|
||||
@@ -102,7 +102,7 @@ public class FSkin {
|
||||
allSkins = new Array<>();
|
||||
final Array<String> skinDirectoryNames = getSkinDirectoryNames();
|
||||
for (final String skinDirectoryName : skinDirectoryNames) {
|
||||
allSkins.add(WordUtils.capitalize(skinDirectoryName.replace('_', ' ')));
|
||||
allSkins.add(WordUtil.capitalize(skinDirectoryName.replace('_', ' ')));
|
||||
}
|
||||
allSkins.sort();
|
||||
}
|
||||
|
||||
@@ -47,15 +47,10 @@ import forge.util.FileUtil;
|
||||
import forge.util.ThreadUtil;
|
||||
import forge.util.Utils;
|
||||
import forge.util.gui.SOptionPane;
|
||||
import forge.util.storage.IStorage;
|
||||
|
||||
import org.apache.commons.lang3.text.WordUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -282,18 +277,12 @@ public class NewQuestScreen extends FScreen {
|
||||
|
||||
cbAllowUnlocks.setSelected(true);
|
||||
|
||||
final Map<String, String> preconDescriptions = new HashMap<>();
|
||||
IStorage<PreconDeck> preconDecks = QuestController.getPrecons();
|
||||
|
||||
for (PreconDeck preconDeck : preconDecks) {
|
||||
for (PreconDeck preconDeck : QuestController.getPrecons()) {
|
||||
if (QuestController.getPreconDeals(preconDeck).getMinWins() > 0) {
|
||||
continue;
|
||||
}
|
||||
String name = preconDeck.getName();
|
||||
cbxPreconDeck.addItem(name);
|
||||
String description = preconDeck.getDescription();
|
||||
description = "<html>" + WordUtils.wrap(description, 40, "<br>", false) + "</html>";
|
||||
preconDescriptions.put(name, description);
|
||||
}
|
||||
|
||||
// disable the very powerful sets -- they can be unlocked later for a high price
|
||||
|
||||
@@ -3,7 +3,7 @@ ManaCost:3 B B
|
||||
Types:Legendary Creature Human Knight
|
||||
PT:5/4
|
||||
T:Mode$ ChangesZone | Origin$ Any | Destination$ Graveyard | ValidCard$ Creature.Other | TriggerZones$ Battlefield | Execute$ TrigDmg | TriggerDescription$ Whenever another creature dies, or a creature card is put into a graveyard from anywhere than the battlefield, or a creature card leaves your graveyard, CARDNAME deals 1 damage to each opponent.
|
||||
T:Mode$ ChangesZone | Origin$ Graveyard | Destination$ Any | ValidCard$ Card.Creature+Other | TriggerZones$ Battlefield | Execute$ TrigDmg | Secondary$ True | TriggerDescription$ Whenever another creature dies, or a creature card is put into a graveyard from anywhere than the battlefield, or a creature card leaves your graveyard, CARDNAME deals 1 damage to each opponent.
|
||||
T:Mode$ ChangesZone | Origin$ Graveyard | Destination$ Any | ValidCard$ Card.Creature+Other+YouOwn | TriggerZones$ Battlefield | Execute$ TrigDmg | Secondary$ True | TriggerDescription$ Whenever another creature dies, or a creature card is put into a graveyard from anywhere than the battlefield, or a creature card leaves your graveyard, CARDNAME deals 1 damage to each opponent.
|
||||
SVar:TrigDmg:DB$ DealDamage | Defined$ Player.Opponent | NumDmg$ 1
|
||||
A:AB$ Mill | Cost$ 1 B | NumCards$ 1 | Defined$ Player | SpellDescription$ Each player puts the top card of their library into their graveyard.
|
||||
AI:RemoveDeck:All
|
||||
|
||||
@@ -102,6 +102,7 @@ cbpGameLogEntryType=Game Log Verbosity
|
||||
cbpCloseAction=Close Action
|
||||
cbpDefaultFontSize=Default Font Size
|
||||
cbpAiProfiles=AI Personality
|
||||
cbpStackAdditions=Stack effect notifications
|
||||
cbpDisplayCurrentCardColors=Show Detailed Card Color
|
||||
cbpAutoYieldMode=Auto-Yield
|
||||
cbpCounterDisplayType=Counter Display Type
|
||||
@@ -116,6 +117,7 @@ nlUseSentry=When enabled, automatically submits bug reports to developers.
|
||||
GamePlay=Gameplay
|
||||
nlpMulliganRule=Choose the version of the Mulligan rule
|
||||
nlpAiProfiles=Choose your AI opponent
|
||||
nlpStackAdditions=Choose when you want to get visual notifications for an effect added to the stack: Never, always, or only for the effects cast/activated by a AI player or triggered by any player
|
||||
nlAnte=Determines whether or not the game is played for ante.
|
||||
nlAnteMatchRarity=Attempts to make antes the same rarity for all players.
|
||||
nlEnableAICheats=Allow the AI to cheat to gain advantage (for personalities that have cheat shuffling options set).
|
||||
|
||||
@@ -239,8 +239,18 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
|
||||
|
||||
@Override
|
||||
public Void visit(final GameEventSpellAbilityCast event) {
|
||||
needStackUpdate = true;
|
||||
return processEvent();
|
||||
needStackUpdate = true;
|
||||
processEvent();
|
||||
|
||||
final Runnable notifyStackAddition = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
matchController.notifyStackAddition(event);
|
||||
}
|
||||
};
|
||||
GuiBase.getInterface().invokeInEdtLater(notifyStackAddition);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -252,7 +262,17 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
|
||||
@Override
|
||||
public Void visit(final GameEventSpellRemovedFromStack event) {
|
||||
needStackUpdate = true;
|
||||
return processEvent();
|
||||
processEvent();
|
||||
|
||||
final Runnable notifyStackAddition = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
matchController.notifyStackRemoval(event);
|
||||
}
|
||||
};
|
||||
GuiBase.getInterface().invokeInEdtLater(notifyStackAddition);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -340,10 +360,10 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
|
||||
if(event.to.getZoneType() == ZoneType.Battlefield)
|
||||
refreshFieldUpdate = true;
|
||||
//pfps the change to the zones have already been performed with add and remove calls
|
||||
// this is only for playing a sound
|
||||
// updateZone(event.from);
|
||||
// this is only for playing a sound
|
||||
// updateZone(event.from);
|
||||
//return updateZone(event.to);
|
||||
return processEvent();
|
||||
return processEvent();
|
||||
|
||||
}
|
||||
|
||||
@@ -373,9 +393,9 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base<Void> {
|
||||
@Override
|
||||
public Void visit(final GameEventShuffle event) {
|
||||
//pfps the change to the library has already been performed by a setCards call
|
||||
// this is only for playing a sound
|
||||
// return updateZone(event.player.getZone(ZoneType.Library));
|
||||
return processEvent();
|
||||
// this is only for playing a sound
|
||||
// return updateZone(event.player.getZone(ZoneType.Library));
|
||||
return processEvent();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -264,12 +264,8 @@ public abstract class GuiDownloadService implements Runnable {
|
||||
count++;
|
||||
cardSkipped = true; //assume skipped unless saved successfully
|
||||
String url = kv.getValue();
|
||||
/*
|
||||
* decode URL Key, Reverted to old version,
|
||||
* on Android 6.0 it throws an error
|
||||
* when you download the card price
|
||||
*/
|
||||
String decodedKey = URLDecoder.decode(kv.getKey());
|
||||
|
||||
String decodedKey = decodeURL(kv.getKey());
|
||||
final File fileDest = new File(decodedKey);
|
||||
final String filePath = fileDest.getPath();
|
||||
final String subLastIndex = filePath.contains("pics") ? "\\pics\\" : "\\db\\";
|
||||
@@ -365,6 +361,16 @@ public abstract class GuiDownloadService implements Runnable {
|
||||
GuiBase.getInterface().preventSystemSleep(false);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private static String decodeURL(String key) {
|
||||
/*
|
||||
* decode URL Key, Reverted to old version,
|
||||
* on Android 6.0 it throws an error
|
||||
* when you download the card price
|
||||
*/
|
||||
return URLDecoder.decode(key);
|
||||
}
|
||||
|
||||
protected Proxy getProxy() {
|
||||
if (type == 0) {
|
||||
return Proxy.NO_PROXY;
|
||||
@@ -385,7 +391,7 @@ public abstract class GuiDownloadService implements Runnable {
|
||||
|
||||
protected static void addMissingItems(Map<String, String> list, String nameUrlFile, String dir) {
|
||||
for (Pair<String, String> nameUrlPair : FileUtil.readNameUrlFile(nameUrlFile)) {
|
||||
File f = new File(dir, URLDecoder.decode(nameUrlPair.getLeft()));
|
||||
File f = new File(dir, decodeURL(nameUrlPair.getLeft()));
|
||||
//System.out.println(f.getAbsolutePath());
|
||||
if (!f.exists()) {
|
||||
list.put(f.getAbsolutePath(), nameUrlPair.getRight());
|
||||
|
||||
@@ -12,6 +12,8 @@ import forge.deck.CardPool;
|
||||
import forge.game.GameEntityView;
|
||||
import forge.game.GameView;
|
||||
import forge.game.card.CardView;
|
||||
import forge.game.event.GameEventSpellAbilityCast;
|
||||
import forge.game.event.GameEventSpellRemovedFromStack;
|
||||
import forge.game.phase.PhaseType;
|
||||
import forge.game.player.DelayedReveal;
|
||||
import forge.game.player.IHasIcon;
|
||||
@@ -47,6 +49,8 @@ public interface IGuiGame {
|
||||
void showManaPool(PlayerView player);
|
||||
void hideManaPool(PlayerView player);
|
||||
void updateStack();
|
||||
void notifyStackAddition(final GameEventSpellAbilityCast event);
|
||||
void notifyStackRemoval(final GameEventSpellRemovedFromStack event);
|
||||
Iterable<PlayerZoneUpdate> tempShowZones(PlayerView controller, Iterable<PlayerZoneUpdate> zonesToUpdate);
|
||||
void hideZones(PlayerView controller, Iterable<PlayerZoneUpdate> zonesToUpdate);
|
||||
void updateZones(Iterable<PlayerZoneUpdate> zonesToUpdate);
|
||||
|
||||
@@ -24,6 +24,8 @@ import forge.assets.FSkinProp;
|
||||
import forge.game.GameView;
|
||||
import forge.game.card.CardView;
|
||||
import forge.game.card.CardView.CardStateView;
|
||||
import forge.game.event.GameEventSpellAbilityCast;
|
||||
import forge.game.event.GameEventSpellRemovedFromStack;
|
||||
import forge.game.player.PlayerView;
|
||||
import forge.interfaces.IGameController;
|
||||
import forge.interfaces.IGuiGame;
|
||||
@@ -251,16 +253,16 @@ public abstract class AbstractGuiGame implements IGuiGame, IMayViewCards {
|
||||
|
||||
private final Set<CardView> selectableCards = Sets.newHashSet();
|
||||
public void setSelectables(final Iterable<CardView> cards) {
|
||||
for ( CardView cv : cards ) { selectableCards.add(cv); }
|
||||
for ( CardView cv : cards ) { selectableCards.add(cv); }
|
||||
}
|
||||
public void clearSelectables() {
|
||||
selectableCards.clear();
|
||||
selectableCards.clear();
|
||||
}
|
||||
public boolean isSelectable(final CardView card) {
|
||||
return selectableCards.contains(card);
|
||||
return selectableCards.contains(card);
|
||||
}
|
||||
public boolean isSelecting() {
|
||||
return !selectableCards.isEmpty();
|
||||
return !selectableCards.isEmpty();
|
||||
}
|
||||
public boolean isGamePaused() { return gamePause; }
|
||||
public void setgamePause(boolean pause) { gamePause = pause; }
|
||||
@@ -694,6 +696,14 @@ public abstract class AbstractGuiGame implements IGuiGame, IMayViewCards {
|
||||
final String yesButtonText, final String noButtonText) {
|
||||
return showConfirmDialog(message, title, yesButtonText, noButtonText, true);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void notifyStackAddition(GameEventSpellAbilityCast event) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyStackRemoval(GameEventSpellRemovedFromStack event) {
|
||||
}
|
||||
|
||||
// End of Choice code
|
||||
}
|
||||
|
||||
@@ -310,6 +310,11 @@ public final class ForgeConstants {
|
||||
public static final String GRAVEYARD_ORDERING_OWN_CARDS = "With Relevant Cards";
|
||||
public static final String GRAVEYARD_ORDERING_ALWAYS = "Always";
|
||||
|
||||
// Constants for Stack effect addition notification policy
|
||||
public static final String STACK_EFFECT_NOTIFICATION_NEVER = "Never";
|
||||
public static final String STACK_EFFECT_NOTIFICATION_ALWAYS = "Always";
|
||||
public static final String STACK_EFFECT_NOTIFICATION_AI_AND_TRIGGERED = "AI cast/activated, or triggered by any player";
|
||||
|
||||
// Set boolean constant for landscape mode for gdx port
|
||||
public static final boolean isGdxPortLandscape = FileUtil.doesFileExist(ASSETS_DIR + "switch_orientation.ini");
|
||||
|
||||
|
||||
@@ -119,6 +119,7 @@ public class ForgePreferences extends PreferencesStore<ForgePreferences.FPref> {
|
||||
UI_HIDE_GAME_TABS ("false"), // Visibility of tabs in match screen.
|
||||
UI_CLOSE_ACTION ("NONE"),
|
||||
UI_MANA_LOST_PROMPT ("false"), // Prompt on losing mana when passing priority
|
||||
UI_STACK_EFFECT_NOTIFICATION_POLICY ("Never"),
|
||||
UI_PAUSE_WHILE_MINIMIZED("false"),
|
||||
UI_TOKENS_IN_SEPARATE_ROW("false"), // Display tokens in their own battlefield row.
|
||||
UI_DISPLAY_CURRENT_COLORS(ForgeConstants.DISP_CURRENT_COLORS_NEVER),
|
||||
|
||||
87
forge-gui/src/main/java/forge/util/WordUtil.java
Normal file
87
forge-gui/src/main/java/forge/util/WordUtil.java
Normal file
@@ -0,0 +1,87 @@
|
||||
package forge.util;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class WordUtil {
|
||||
public static String capitalize(String str) {
|
||||
if (StringUtils.isEmpty(str)) {
|
||||
return str;
|
||||
}
|
||||
final char[] buffer = str.toCharArray();
|
||||
boolean capitalizeNext = true;
|
||||
for (int i = 0; i < buffer.length; i++) {
|
||||
final char ch = buffer[i];
|
||||
if (Character.isWhitespace(ch)) {
|
||||
capitalizeNext = true;
|
||||
} else if (capitalizeNext) {
|
||||
buffer[i] = Character.toTitleCase(ch);
|
||||
capitalizeNext = false;
|
||||
}
|
||||
}
|
||||
return new String(buffer);
|
||||
}
|
||||
|
||||
private final static Pattern patternToWrapOn = Pattern.compile(" ");
|
||||
public static String wordWrapAsHTML(String str) {
|
||||
String result = null;
|
||||
int wrapLength = 40;
|
||||
String newLineStr = "<br>";
|
||||
if (str != null) {
|
||||
final int inputLineLength = str.length();
|
||||
int offset = 0;
|
||||
final StringBuilder wrappedLine = new StringBuilder(inputLineLength + 32);
|
||||
while (offset < inputLineLength) {
|
||||
int spaceToWrapAt = -1;
|
||||
Matcher matcher = patternToWrapOn.matcher(str.substring(offset, Math.min(offset + wrapLength + 1, inputLineLength)));
|
||||
if (matcher.find()) {
|
||||
if (matcher.start() == 0) {
|
||||
offset += matcher.end();
|
||||
continue;
|
||||
}
|
||||
spaceToWrapAt = matcher.start() + offset;
|
||||
}
|
||||
|
||||
// only last line without leading spaces is left
|
||||
if (inputLineLength - offset <= wrapLength) {
|
||||
break;
|
||||
}
|
||||
|
||||
while (matcher.find()) {
|
||||
spaceToWrapAt = matcher.start() + offset;
|
||||
}
|
||||
|
||||
if (spaceToWrapAt >= offset) {
|
||||
// normal case
|
||||
wrappedLine.append(str, offset, spaceToWrapAt);
|
||||
wrappedLine.append(newLineStr);
|
||||
offset = spaceToWrapAt + 1;
|
||||
|
||||
} else {
|
||||
// do not wrap really long word, just extend beyond limit
|
||||
matcher = patternToWrapOn.matcher(str.substring(offset + wrapLength));
|
||||
if (matcher.find()) {
|
||||
spaceToWrapAt = matcher.start() + offset + wrapLength;
|
||||
}
|
||||
|
||||
if (spaceToWrapAt >= 0) {
|
||||
wrappedLine.append(str, offset, spaceToWrapAt);
|
||||
wrappedLine.append(newLineStr);
|
||||
offset = spaceToWrapAt + 1;
|
||||
} else {
|
||||
wrappedLine.append(str, offset, str.length());
|
||||
offset = inputLineLength;
|
||||
}
|
||||
}
|
||||
}// Whatever is left in line is short enough to just pass through
|
||||
wrappedLine.append(str, offset, str.length());
|
||||
result = wrappedLine.toString();
|
||||
}
|
||||
|
||||
return "<html>" + result + "</html>";
|
||||
}
|
||||
|
||||
private WordUtil() {}
|
||||
}
|
||||
Reference in New Issue
Block a user