/** * Copyright (c) 2005-2017, KoLmafia development team * http://kolmafia.sourceforge.net/ * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * [1] Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * [2] Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * [3] Neither the name "KoLmafia" nor the names of its contributors may * be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package net.sourceforge.kolmafia.swingui.widget; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.Map.Entry; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.swing.JList; import javax.swing.JPopupMenu; import javax.swing.JSeparator; import javax.swing.SwingUtilities; import net.java.dev.spellcast.utilities.LockableListModel; import net.java.dev.spellcast.utilities.LockableListModel.ListElementFilter; import net.sourceforge.kolmafia.AdventureResult; import net.sourceforge.kolmafia.CreateFrameRunnable; import net.sourceforge.kolmafia.KoLConstants; import net.sourceforge.kolmafia.KoLmafiaCLI; import net.sourceforge.kolmafia.Modifiers; import net.sourceforge.kolmafia.RequestThread; import net.sourceforge.kolmafia.StaticEntity; import net.sourceforge.kolmafia.maximizer.Boost; import net.sourceforge.kolmafia.moods.MoodManager; import net.sourceforge.kolmafia.moods.MoodTrigger; import net.sourceforge.kolmafia.objectpool.Concoction; import net.sourceforge.kolmafia.objectpool.ItemPool; import net.sourceforge.kolmafia.persistence.ConcoctionDatabase.QueuedConcoction; import net.sourceforge.kolmafia.persistence.EffectDatabase; import net.sourceforge.kolmafia.persistence.ItemDatabase; import net.sourceforge.kolmafia.persistence.SkillDatabase; import net.sourceforge.kolmafia.preferences.Preferences; import net.sourceforge.kolmafia.request.AutoMallRequest; import net.sourceforge.kolmafia.request.AutoSellRequest; import net.sourceforge.kolmafia.request.CreateItemRequest; import net.sourceforge.kolmafia.request.PulverizeRequest; import net.sourceforge.kolmafia.request.PurchaseRequest; import net.sourceforge.kolmafia.request.UneffectRequest; import net.sourceforge.kolmafia.request.UseItemRequest; import net.sourceforge.kolmafia.request.UseSkillRequest; import net.sourceforge.kolmafia.swingui.CommandDisplayFrame; import net.sourceforge.kolmafia.swingui.MallSearchFrame; import net.sourceforge.kolmafia.swingui.ProfileFrame; import net.sourceforge.kolmafia.swingui.listener.ThreadedListener; import net.sourceforge.kolmafia.swingui.menu.ThreadedMenuItem; import net.sourceforge.kolmafia.utilities.CharacterEntities; import net.sourceforge.kolmafia.utilities.InputFieldUtilities; import net.sourceforge.kolmafia.utilities.StringUtilities; import net.sourceforge.kolmafia.utilities.WikiUtilities; import net.sourceforge.kolmafia.webui.RelayLoader; public class ShowDescriptionList extends JList { public int lastSelectIndex; public JPopupMenu contextMenu; public ListElementFilter filter; private final LockableListModel displayModel, originalModel; private static final Pattern PLAYERID_MATCHER = Pattern.compile( "\\(#(\\d+)\\)" ); public ShowDescriptionList( final LockableListModel displayModel ) { this( displayModel, null, 4 ); } public ShowDescriptionList( final LockableListModel displayModel, final int visibleRowCount ) { this( displayModel, null, visibleRowCount ); } public ShowDescriptionList( final LockableListModel displayModel, final ListElementFilter filter ) { this( displayModel, filter, 4 ); } public ShowDescriptionList( final LockableListModel displayModel, final ListElementFilter filter, final int visibleRowCount ) { this.contextMenu = new JPopupMenu(); boolean isMoodList = displayModel == MoodManager.getTriggers(); boolean isEncyclopedia = !displayModel.isEmpty() && displayModel.get( 0 ) instanceof Entry; if ( !isMoodList ) { if ( displayModel.isEmpty() || !isEncyclopedia ) { this.contextMenu.add( new ContextMenuItem( "Game description", new DescriptionRunnable() ) ); } this.contextMenu.add( new ContextMenuItem( "Wiki description", new WikiLookupRunnable() ) ); } if ( displayModel == MallSearchFrame.results ) { this.contextMenu.add( new JSeparator() ); this.contextMenu.add( new ContextMenuItem( "Go To Store...", new StoreLookupRunnable() ) ); } if ( displayModel == KoLConstants.activeEffects ) { this.contextMenu.add( new ContextMenuItem( "Remove this effect", new ShrugOffRunnable() ) ); this.contextMenu.add( new ContextMenuItem( "Add to current mood", new AddToMoodEffectRunnable() ) ); this.contextMenu.add( new ContextMenuItem( "Extend this effect", new ExtendEffectRunnable() ) ); } if ( displayModel == KoLConstants.usableSkills || displayModel == KoLConstants.availableSkills ) { this.contextMenu.add( new ContextMenuItem( "Cast the skill once", new CastSkillRunnable() ) ); this.contextMenu.add( new ContextMenuItem( "Add to current mood", new AddToMoodSkillRunnable() ) ); } if ( displayModel == KoLConstants.tally ) { this.contextMenu.add( new ContextMenuItem( "Zero out entries", new ZeroTallyRunnable() ) ); this.contextMenu.add( new JSeparator() ); this.contextMenu.add( new ContextMenuItem( "Add to junk list", new AddToJunkListRunnable() ) ); this.contextMenu.add( new ContextMenuItem( "Add to singleton list", new AddToSingletonListRunnable() ) ); this.contextMenu.add( new ContextMenuItem( "Add to memento list", new AddToMementoListRunnable() ) ); this.contextMenu.add( new JSeparator() ); this.contextMenu.add( new ContextMenuItem( "Autosell selected", new AutoSellRunnable() ) ); this.contextMenu.add( new ContextMenuItem( "Add selected to mall", new AutoMallRunnable() ) ); this.contextMenu.add( new ContextMenuItem( "Consume selected", new ConsumeRunnable() ) ); this.contextMenu.add( new ContextMenuItem( "Pulverize selected", new PulverizeRunnable() ) ); } else if ( displayModel == KoLConstants.inventory || displayModel == KoLConstants.closet || isEncyclopedia ) { this.contextMenu.add( new ContextMenuItem( "Add to junk list", new AddToJunkListRunnable() ) ); this.contextMenu.add( new ContextMenuItem( "Add to singleton list", new AddToSingletonListRunnable() ) ); this.contextMenu.add( new ContextMenuItem( "Add to memento list", new AddToMementoListRunnable() ) ); } else if ( isMoodList ) { this.contextMenu.add( new ContextMenuItem( "Force execution", new ForceExecuteRunnable() ) ); this.contextMenu.add( new ContextMenuItem( "Remove selected", new RemoveTriggerRunnable() ) ); this.addKeyListener( new RemoveTriggerListener() ); } this.addMouseListener( new PopupListener() ); this.originalModel = displayModel; this.displayModel = filter == null ? displayModel.getMirrorImage() : displayModel.getMirrorImage( filter ); this.setModel( this.displayModel ); this.setVisibleRowCount( visibleRowCount ); this.setCellRenderer( ListCellRendererFactory.getDefaultRenderer() ); this.setPrototypeCellValue( "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" ); } public LockableListModel getOriginalModel() { return this.originalModel; } public AdventureResult[] getSelectedItems() { // Obviously, this only works if the model contains AdventureResults Object[] values = this.getSelectedValues(); AdventureResult[] result = new AdventureResult[ values.length ]; for ( int i = 0; i < values.length; ++i ) { result[ i ] = (AdventureResult)values[ i ]; } return result; } public PurchaseRequest[] getSelectedPurchases() { // Obviously, this only works if the model contains PurchaseRequests Object[] values = this.getSelectedValues(); PurchaseRequest[] result = new PurchaseRequest[ values.length ]; for ( int i = 0; i < values.length; ++i ) { result[ i ] = (PurchaseRequest)values[ i ]; } return result; } /** * Shows and hides the applicable context menu item. Actually all it does is show it -- the VM will handle hiding * it. */ private class PopupListener extends MouseAdapter { @Override public void mousePressed( final MouseEvent e ) { this.maybeShowPopup( e ); } @Override public void mouseReleased( final MouseEvent e ) { this.maybeShowPopup( e ); } private void maybeShowPopup( final MouseEvent e ) { if ( e.isPopupTrigger() ) { int index = ShowDescriptionList.this.locationToIndex( e.getPoint() ); ShowDescriptionList.this.lastSelectIndex = index; if ( !ShowDescriptionList.this.isSelectedIndex( index ) ) { ShowDescriptionList.this.clearSelection(); ShowDescriptionList.this.addSelectionInterval( index, index ); } ShowDescriptionList.this.contextMenu.show( e.getComponent(), e.getX(), e.getY() ); } } } public static final void showGameDescription( Object item ) { if ( item instanceof Boost ) { item = ((Boost) item).getItem(); } if ( item instanceof AdventureResult ) { if ( ( (AdventureResult) item ).isItem() ) { StaticEntity.openDescriptionFrame( "desc_item.php?whichitem=" + ItemDatabase.getDescriptionId( ( (AdventureResult) item ).getItemId() ) ); } if ( ( (AdventureResult) item ).isStatusEffect() ) { StaticEntity.openDescriptionFrame( "desc_effect.php?whicheffect=" + EffectDatabase.getDescriptionId( EffectDatabase.getEffectId( ( (AdventureResult) item ).getName() ) ) ); } } else if ( item instanceof Concoction ) { StaticEntity.openDescriptionFrame( "desc_item.php?whichitem=" + ItemDatabase.getDescriptionId( ( (Concoction) item ).getItemId() ) ); } else if ( item instanceof QueuedConcoction ) { StaticEntity.openDescriptionFrame( "desc_item.php?whichitem=" + ItemDatabase.getDescriptionId( ( (QueuedConcoction) item ).getItemId() ) ); } else if ( item instanceof CreateItemRequest ) { StaticEntity.openDescriptionFrame( "desc_item.php?whichitem=" + ItemDatabase.getDescriptionId( ( (CreateItemRequest) item ).getItemId() ) ); } else if ( item instanceof PurchaseRequest ) { StaticEntity.openDescriptionFrame( "desc_item.php?whichitem=" + ItemDatabase.getDescriptionId( ( (PurchaseRequest) item ).getItemId() ) ); } else if ( item instanceof UseSkillRequest ) { StaticEntity.openDescriptionFrame( "desc_skill.php?whichskill=" + ( (UseSkillRequest) item ).getSkillId() + "&self=true" ); } else if ( item instanceof String ) { Matcher playerMatcher = ShowDescriptionList.PLAYERID_MATCHER.matcher( (String) item ); if ( playerMatcher.find() ) { Object[] parameters = new Object[] { "#" + playerMatcher.group( 1 ) }; SwingUtilities.invokeLater( new CreateFrameRunnable( ProfileFrame.class, parameters ) ); } } } public static void showMallStore( Object item ) { if ( item instanceof PurchaseRequest ) { RelayLoader.openSystemBrowser( "mallstore.php?whichstore=" + ( (PurchaseRequest) item ).getFormField( "whichstore" ) ); } } private class ContextMenuItem extends ThreadedMenuItem { public ContextMenuItem( final String title, final ThreadedListener action ) { super( title, action ); } } private abstract class ContextMenuListener extends ThreadedListener { public int index; public Object item; @Override protected void execute() { this.index = ShowDescriptionList.this.lastSelectIndex == -1 ? ShowDescriptionList.this.getSelectedIndex() : ShowDescriptionList.this.lastSelectIndex; this.item = ShowDescriptionList.this.displayModel.getElementAt( this.index ); if ( this.item == null ) { return; } ShowDescriptionList.this.ensureIndexIsVisible( this.index ); this.executeAction(); } protected abstract void executeAction(); } /** * Utility class which shows the description of the item which is currently selected. */ private class DescriptionRunnable extends ContextMenuListener { @Override public void executeAction() { ShowDescriptionList.showGameDescription( this.item ); } } /** * Utility class which shows the description of the item which is currently selected, as it appears on the wiki. */ private class WikiLookupRunnable extends ContextMenuListener { @Override public void executeAction() { WikiUtilities.showWikiDescription( this.item ); } } public class StoreLookupRunnable extends ContextMenuListener { @Override protected void executeAction() { ShowDescriptionList.showMallStore( this.item ); } } public void removeTriggers() { Object[] items = ShowDescriptionList.this.getSelectedValues(); ShowDescriptionList.this.clearSelection(); MoodManager.removeTriggers( items ); MoodManager.saveSettings(); } private class ForceExecuteRunnable extends ContextMenuListener { @Override public void executeAction() { Object[] items = ShowDescriptionList.this.getSelectedValues(); ShowDescriptionList.this.clearSelection(); for ( int i = 0; i < items.length; ++i ) { KoLmafiaCLI.DEFAULT_SHELL.executeLine( ( (MoodTrigger) items[ i ] ).getAction() ); } } } private class RemoveTriggerRunnable extends ContextMenuListener { @Override public void executeAction() { ShowDescriptionList.this.removeTriggers(); } } private class RemoveTriggerListener extends KeyAdapter { @Override public void keyReleased( final KeyEvent e ) { if ( e.isConsumed() ) { return; } if ( e.getKeyCode() != KeyEvent.VK_DELETE && e.getKeyCode() != KeyEvent.VK_BACK_SPACE ) { return; } ShowDescriptionList.this.removeTriggers(); e.consume(); } } private class CastSkillRunnable extends ContextMenuListener { @Override public void executeAction() { Object[] skills = ShowDescriptionList.this.getSelectedValues(); ShowDescriptionList.this.clearSelection(); UseSkillRequest request; for ( int i = 0; i < skills.length; ++i ) { request = (UseSkillRequest) skills[ i ]; request.setTarget( null ); request.setBuffCount( 1 ); RequestThread.postRequest( request ); } } } private class AddToMoodSkillRunnable extends ContextMenuListener { @Override public void executeAction() { Object[] skills = ShowDescriptionList.this.getSelectedValues(); ShowDescriptionList.this.clearSelection(); if ( Preferences.getString( "currentMood" ).equals( "apathetic" ) ) { Preferences.setString( "currentMood", "default" ); } String name, action; for ( int i = 0; i < skills.length; ++i ) { name = UneffectRequest.skillToEffect( ( (UseSkillRequest) skills[ i ] ).getSkillName() ); action = MoodManager.getDefaultAction( "lose_effect", name ); if ( !action.equals( "" ) ) { MoodManager.addTrigger( "lose_effect", name, action ); } } MoodManager.saveSettings(); } } private class AddToMoodEffectRunnable extends ContextMenuListener { @Override public void executeAction() { Object[] effects = ShowDescriptionList.this.getSelectedValues(); ShowDescriptionList.this.clearSelection(); if ( Preferences.getString( "currentMood" ).equals( "apathetic" ) ) { Preferences.setString( "currentMood", "default" ); } String name, action; for ( int i = 0; i < effects.length; ++i ) { name = ( (AdventureResult) effects[ i ] ).getName(); action = MoodManager.getDefaultAction( "lose_effect", name ); if ( !action.equals( "" ) ) { MoodManager.addTrigger( "lose_effect", name, action ); continue; } action = MoodManager.getDefaultAction( "gain_effect", name ); if ( !action.equals( "" ) ) { MoodManager.addTrigger( "gain_effect", name, action ); } } MoodManager.saveSettings(); } } private class ExtendEffectRunnable extends ContextMenuListener { @Override public void executeAction() { Object[] effects = ShowDescriptionList.this.getSelectedValues(); ShowDescriptionList.this.clearSelection(); String name, action; for ( int i = 0; i < effects.length; ++i ) { name = ( (AdventureResult) effects[ i ] ).getName(); action = MoodManager.getDefaultAction( "lose_effect", name ); if ( !action.equals( "" ) ) { CommandDisplayFrame.executeCommand( action ); } } } } private class ShrugOffRunnable extends ContextMenuListener { @Override public void executeAction() { Object[] effects = ShowDescriptionList.this.getSelectedValues(); for ( int i = 0; i < effects.length; ++i ) { RequestThread.postRequest( new UneffectRequest( (AdventureResult) effects[ i ] ) ); } } } private class AddToJunkListRunnable extends ContextMenuListener { @Override public void executeAction() { Object[] items = ShowDescriptionList.this.getSelectedValues(); ShowDescriptionList.this.clearSelection(); AdventureResult data; for ( int i = 0; i < items.length; ++i ) { data = null; if ( items[ i ] instanceof CreateItemRequest ) { data = ( (CreateItemRequest) items[ i ] ).createdItem; } else if ( items[ i ] instanceof AdventureResult && ( (AdventureResult) items[ i ] ).isItem() ) { data = (AdventureResult) items[ i ]; } else if ( items[ i ] instanceof String && ItemDatabase.contains( (String) items[ i ] ) ) { int itemId = ItemDatabase.getItemId( (String) items[ i ] ); data = ItemPool.get( itemId ); } else if ( items[ i ] instanceof Entry && ItemDatabase.contains( (String) ( (Entry) items[ i ] ).getValue() ) ) { int itemId = ItemDatabase.getItemId( (String) ( (Entry) items[ i ] ).getValue() ); data = ItemPool.get( itemId ); } if ( data == null ) { continue; } if ( !KoLConstants.junkList.contains( data ) ) { KoLConstants.junkList.add( data ); } } } } private class AddToSingletonListRunnable extends ContextMenuListener { @Override public void executeAction() { Object[] items = ShowDescriptionList.this.getSelectedValues(); ShowDescriptionList.this.clearSelection(); AdventureResult data; for ( int i = 0; i < items.length; ++i ) { data = null; if ( items[ i ] instanceof CreateItemRequest ) { data = ( (CreateItemRequest) items[ i ] ).createdItem; } else if ( items[ i ] instanceof AdventureResult && ( (AdventureResult) items[ i ] ).isItem() ) { data = (AdventureResult) items[ i ]; } else if ( items[ i ] instanceof String && ItemDatabase.contains( (String) items[ i ] ) ) { int itemId = ItemDatabase.getItemId( (String) items[ i ] ); data = ItemPool.get( itemId ); } else if ( items[ i ] instanceof Entry && ItemDatabase.contains( (String) ( (Entry) items[ i ] ).getValue() ) ) { int itemId = ItemDatabase.getItemId( (String) ( (Entry) items[ i ] ).getValue() ); data = ItemPool.get( itemId ); } if ( data == null ) { continue; } if ( !KoLConstants.junkList.contains( data ) ) { KoLConstants.junkList.add( data ); } if ( !KoLConstants.singletonList.contains( data ) ) { KoLConstants.singletonList.add( data ); } } } } private class AddToMementoListRunnable extends ContextMenuListener { @Override public void executeAction() { Object[] items = ShowDescriptionList.this.getSelectedValues(); ShowDescriptionList.this.clearSelection(); AdventureResult data; for ( int i = 0; i < items.length; ++i ) { data = null; if ( items[ i ] instanceof CreateItemRequest ) { data = ( (CreateItemRequest) items[ i ] ).createdItem; } else if ( items[ i ] instanceof AdventureResult && ( (AdventureResult) items[ i ] ).isItem() ) { data = (AdventureResult) items[ i ]; } else if ( items[ i ] instanceof String && ItemDatabase.contains( (String) items[ i ] ) ) { int itemId = ItemDatabase.getItemId( (String) items[ i ] ); data = ItemPool.get( itemId ); } else if ( items[ i ] instanceof Entry && ItemDatabase.contains( (String) ( (Entry) items[ i ] ).getValue() ) ) { int itemId = ItemDatabase.getItemId( (String) ( (Entry) items[ i ] ).getValue() ); data = ItemPool.get( itemId ); } if ( data != null && !KoLConstants.mementoList.contains( data ) ) { KoLConstants.mementoList.add( data ); } } Preferences.setBoolean( "mementoListActive", true ); } } private class ZeroTallyRunnable extends ContextMenuListener { @Override public void executeAction() { AdventureResult[] items = ShowDescriptionList.this.getSelectedItems(); for ( int i = 0; i < items.length; ++i ) { AdventureResult.addResultToList( KoLConstants.tally, items[ i ].getNegation() ); } } } private class AutoSellRunnable extends ContextMenuListener { @Override public void executeAction() { if ( !InputFieldUtilities.confirm( "Are you sure you would like to sell the selected items?" ) ) { return; } AdventureResult[] items = ShowDescriptionList.this.getSelectedItems(); RequestThread.postRequest( new AutoSellRequest( items ) ); } } private class AutoMallRunnable extends ContextMenuListener { @Override public void executeAction() { if ( !InputFieldUtilities.confirm( "Are you sure you would like to add the selected items to your store?" ) ) { return; } AdventureResult[] items = ShowDescriptionList.this.getSelectedItems(); RequestThread.postRequest( new AutoMallRequest( items ) ); } } private class ConsumeRunnable extends ContextMenuListener { @Override public void executeAction() { if ( !InputFieldUtilities.confirm( "Are you sure you want to use the selected items?" ) ) { return; } AdventureResult[] items = ShowDescriptionList.this.getSelectedItems(); for ( int i = 0; i < items.length; ++i ) { RequestThread.postRequest( UseItemRequest.getInstance( items[ i ] ) ); } } } private class PulverizeRunnable extends ContextMenuListener { @Override public void executeAction() { if ( !InputFieldUtilities.confirm( "The items you've selected will be smashed to pieces. Are you sure?" ) ) { return; } AdventureResult[] items = ShowDescriptionList.this.getSelectedItems(); for ( int i = 0; i < items.length; ++i ) { RequestThread.postRequest( new PulverizeRequest( items[ i ] ) ); } } } }