/* * Copyright (c) 1998-2017 by Richard A. Wilkes. All rights reserved. * * This Source Code Form is subject to the terms of the Mozilla Public * License, version 2.0. If a copy of the MPL was not distributed with * this file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This Source Code Form is "Incompatible With Secondary Licenses", as * defined by the Mozilla Public License, version 2.0. */ package com.trollworks.gcs.spell; import com.trollworks.gcs.character.GURPSCharacter; import com.trollworks.gcs.common.DataFile; import com.trollworks.gcs.common.ListFile; import com.trollworks.gcs.library.LibraryFile; import com.trollworks.gcs.menu.edit.Incrementable; import com.trollworks.gcs.menu.edit.SkillLevelIncrementable; import com.trollworks.gcs.menu.edit.TechLevelIncrementable; import com.trollworks.gcs.template.Template; import com.trollworks.gcs.widgets.outline.ListOutline; import com.trollworks.gcs.widgets.outline.ListRow; import com.trollworks.gcs.widgets.outline.MultipleRowUndo; import com.trollworks.gcs.widgets.outline.RowPostProcessor; import com.trollworks.gcs.widgets.outline.RowUndo; import com.trollworks.toolkit.annotation.Localize; import com.trollworks.toolkit.collections.FilteredIterator; import com.trollworks.toolkit.ui.widget.outline.OutlineModel; import com.trollworks.toolkit.ui.widget.outline.Row; import com.trollworks.toolkit.utility.Localization; import com.trollworks.toolkit.utility.text.Numbers; import java.awt.EventQueue; import java.awt.dnd.DropTargetDragEvent; import java.util.ArrayList; import java.util.List; /** An outline specifically for spells. */ public class SpellOutline extends ListOutline implements Incrementable, TechLevelIncrementable, SkillLevelIncrementable { @Localize("Increment Points") @Localize(locale = "de", value = "Punkte erhöhen") @Localize(locale = "ru", value = "Увеличить очки") @Localize(locale = "es", value = "Incrementar Puntos") private static String INCREMENT; @Localize("Decrement Points") @Localize(locale = "de", value = "Punkte verringern") @Localize(locale = "ru", value = "Уменьшить очки") @Localize(locale = "es", value = "Disminuir Puntos") private static String DECREMENT; @Localize("Increment Spell Level") @Localize(locale = "ru", value = "Увеличить уровень заклинания") private static String INCREMENT_SPELL_LEVEL; @Localize("Decrement Spell Level") @Localize(locale = "ru", value = "Уменьшить уровень заклинания") private static String DECREMENT_SPELL_LEVEL; static { Localization.initialize(); } private static OutlineModel extractModel(DataFile dataFile) { if (dataFile instanceof GURPSCharacter) { return ((GURPSCharacter) dataFile).getSpellsRoot(); } if (dataFile instanceof Template) { return ((Template) dataFile).getSpellsModel(); } if (dataFile instanceof LibraryFile) { return ((LibraryFile) dataFile).getSpellList().getModel(); } return ((ListFile) dataFile).getModel(); } /** * Create a new spells outline. * * @param dataFile The owning data file. */ public SpellOutline(DataFile dataFile) { this(dataFile, extractModel(dataFile)); } /** * Create a new spells outline. * * @param dataFile The owning data file. * @param model The {@link OutlineModel} to use. */ public SpellOutline(DataFile dataFile, OutlineModel model) { super(dataFile, model, Spell.ID_LIST_CHANGED); SpellColumn.addColumns(this, dataFile); } @Override public String getDecrementTitle() { return DECREMENT; } @Override public String getIncrementTitle() { return INCREMENT; } @Override public boolean canDecrement() { return (mDataFile instanceof GURPSCharacter || mDataFile instanceof Template) && selectionHasLeafRows(true); } @Override public boolean canIncrement() { return (mDataFile instanceof GURPSCharacter || mDataFile instanceof Template) && selectionHasLeafRows(false); } private boolean selectionHasLeafRows(boolean requirePointsAboveZero) { for (Spell spell : new FilteredIterator<>(getModel().getSelectionAsList(), Spell.class)) { if (!spell.canHaveChildren() && (!requirePointsAboveZero || spell.getPoints() > 0)) { return true; } } return false; } @SuppressWarnings("unused") @Override public void decrement() { List<RowUndo> undos = new ArrayList<RowUndo>(); for (Spell spell : new FilteredIterator<>(getModel().getSelectionAsList(), Spell.class)) { if (!spell.canHaveChildren()) { int points = spell.getPoints(); if (points > 0) { RowUndo undo = new RowUndo(spell); spell.setPoints(points - 1); if (undo.finish()) { undos.add(undo); } } } } if (!undos.isEmpty()) { repaintSelection(); new MultipleRowUndo(undos); } } @SuppressWarnings("unused") @Override public void increment() { List<RowUndo> undos = new ArrayList<RowUndo>(); for (Spell spell : new FilteredIterator<Spell>(getModel().getSelectionAsList(), Spell.class)) { if (!spell.canHaveChildren()) { RowUndo undo = new RowUndo(spell); spell.setPoints(spell.getPoints() + 1); if (undo.finish()) { undos.add(undo); } } } if (!undos.isEmpty()) { repaintSelection(); new MultipleRowUndo(undos); } } @Override public String getIncrementSkillLevelTitle() { return INCREMENT_SPELL_LEVEL; } @Override public String getDecrementSkillLevelTitle() { return DECREMENT_SPELL_LEVEL; } @Override public boolean canIncrementSkillLevel() { return canIncrement(); } @Override public boolean canDecrementSkillLevel() { return canDecrement(); } @SuppressWarnings("unused") @Override public void incrementSkillLevel() { List<RowUndo> undos = new ArrayList<>(); for (Spell spell : new FilteredIterator<>(getModel().getSelectionAsList(), Spell.class)) { if (!spell.canHaveChildren()) { int basePoints = spell.getPoints() + 1; int maxPoints = basePoints + 4; int oldLevel = spell.getLevel(); RowUndo undo = new RowUndo(spell); for (int points = basePoints; points < maxPoints; points++) { spell.setPoints(points); if (spell.getLevel() > oldLevel) { break; } } // if skill level didn't change, perhaps we hit the limit and should reset points to // old value if (spell.getLevel() == oldLevel) { spell.setPoints(basePoints - 1); } if (undo.finish()) { undos.add(undo); } } } if (!undos.isEmpty()) { repaintSelection(); new MultipleRowUndo(undos); } } @SuppressWarnings("unused") @Override public void decrementSkillLevel() { List<RowUndo> undos = new ArrayList<>(); for (Spell spell : new FilteredIterator<>(getModel().getSelectionAsList(), Spell.class)) { if (!spell.canHaveChildren()) { RowUndo undo = new RowUndo(spell); int oldLevel = spell.getLevel(); int points = spell.getPoints() - 1; spell.setPoints(points); if (spell.getLevel() == -1) { spell.setPoints(0); } else { while (points > 0) { spell.setPoints(--points); if (spell.getLevel() < oldLevel - 1) { spell.setPoints(points + 1); break; } } } if (undo.finish()) { undos.add(undo); } } } if (!undos.isEmpty()) { repaintSelection(); new MultipleRowUndo(undos); } } @Override public boolean canIncrementTechLevel() { return checkTechLevel(0); } @Override public boolean canDecrementTechLevel() { return checkTechLevel(1); } private boolean checkTechLevel(int minLevel) { for (Spell spell : new FilteredIterator<>(getModel().getSelectionAsList(), Spell.class)) { if (!spell.canHaveChildren() && getCurrentTechLevel(spell) >= minLevel) { return true; } } return false; } private static int getCurrentTechLevel(Spell spell) { String tl = spell.getTechLevel(); if (tl != null) { tl = tl.trim(); if (!tl.isEmpty()) { for (int i = tl.length(); --i >= 0;) { if (!Character.isDigit(tl.charAt(i))) { return -1; } } return Numbers.extractInteger(tl, -1, 0, Integer.MAX_VALUE, false); } } return -1; } @SuppressWarnings("unused") @Override public void incrementTechLevel() { List<RowUndo> undos = new ArrayList<>(); for (Spell spell : new FilteredIterator<>(getModel().getSelectionAsList(), Spell.class)) { if (!spell.canHaveChildren()) { int tl = getCurrentTechLevel(spell); if (tl >= 0) { RowUndo undo = new RowUndo(spell); spell.setTechLevel(Integer.toString(tl + 1)); if (undo.finish()) { undos.add(undo); } } } } if (!undos.isEmpty()) { repaintSelection(); new MultipleRowUndo(undos); } } @SuppressWarnings("unused") @Override public void decrementTechLevel() { List<RowUndo> undos = new ArrayList<>(); for (Spell spell : new FilteredIterator<>(getModel().getSelectionAsList(), Spell.class)) { if (!spell.canHaveChildren()) { int tl = getCurrentTechLevel(spell); if (tl >= 1) { RowUndo undo = new RowUndo(spell); spell.setTechLevel(Integer.toString(tl - 1)); if (undo.finish()) { undos.add(undo); } } } } if (!undos.isEmpty()) { repaintSelection(); new MultipleRowUndo(undos); } } @Override protected boolean isRowDragAcceptable(DropTargetDragEvent dtde, Row[] rows) { return !getModel().isLocked() && rows.length > 0 && rows[0] instanceof Spell; } @Override public void convertDragRowsToSelf(List<Row> list) { OutlineModel model = getModel(); Row[] rows = model.getDragRows(); boolean forSheetOrTemplate = mDataFile instanceof GURPSCharacter || mDataFile instanceof Template; ArrayList<ListRow> process = new ArrayList<>(); for (Row element : rows) { Spell spell = new Spell(mDataFile, (Spell) element, true, forSheetOrTemplate); model.collectRowsAndSetOwner(list, spell, false); if (forSheetOrTemplate) { addRowsToBeProcessed(process, spell); } } if (forSheetOrTemplate && !process.isEmpty()) { EventQueue.invokeLater(new RowPostProcessor(this, process)); } } }