/**
* 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.panel;
import java.lang.NullPointerException;
import java.lang.IllegalArgumentException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.RowSorter;
import javax.swing.SortOrder;
import javax.swing.SwingUtilities;
import javax.swing.border.TitledBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;
import net.java.dev.spellcast.utilities.LockableListModel;
import net.java.dev.spellcast.utilities.LockableListModel.ListElementFilter;
import net.sourceforge.kolmafia.KoLCharacter;
import net.sourceforge.kolmafia.KoLmafia;
import net.sourceforge.kolmafia.RequestThread;
import net.sourceforge.kolmafia.listener.Listener;
import net.sourceforge.kolmafia.listener.NamedListenerRegistry;
import net.sourceforge.kolmafia.objectpool.EffectPool;
import net.sourceforge.kolmafia.objectpool.IntegerPool;
import net.sourceforge.kolmafia.objectpool.ItemPool;
import net.sourceforge.kolmafia.persistence.CandyDatabase;
import net.sourceforge.kolmafia.persistence.CandyDatabase.Candy;
import net.sourceforge.kolmafia.persistence.EffectDatabase;
import net.sourceforge.kolmafia.persistence.ItemDatabase;
import net.sourceforge.kolmafia.persistence.MallPriceDatabase;
import net.sourceforge.kolmafia.request.SweetSynthesisRequest;
import net.sourceforge.kolmafia.session.StoreManager;
import net.sourceforge.kolmafia.swingui.listener.ThreadedListener;
import com.jgoodies.binding.adapter.AbstractTableAdapter;
public class SynthesizePanel
extends JPanel
implements ActionListener, Listener
{
// The panel with the effect buttons
private EffectPanel effectPanel;
// The filter checkboxes
private JCheckBox[] filters;
private boolean availableChecked = false;
private boolean allowedChecked = false;;
// The panel with Candy A and Candy B columns
private CandyPanel candyPanel;
private CandyPanel.CandyList candyList1;
private CandyPanel.CandyList candyList2;
// The Buttons
private JButton synthesizeButton;
private JButton automaticButton;
private JButton priceCheckButton;
// The panel with data about Candy A and Candy B and total cost/turn
private CandyDataPanel candyData;
// How old is "too old" for a price. Expressed in fractional days.
public static final float AGE_LIMIT = ( 60.0f * 60.0f ) / 86400.0f; // One hour
public SynthesizePanel()
{
super();
JPanel centerPanel = new JPanel( new BorderLayout( 0, 0 ) );
JPanel northPanel = new JPanel();
northPanel.setLayout( new BoxLayout( northPanel, BoxLayout.Y_AXIS ) );
this.effectPanel = new EffectPanel();
northPanel.add( this.effectPanel );
northPanel.add( this.addFilters() );
centerPanel.add( northPanel, BorderLayout.NORTH );
candyPanel = new CandyPanel();
centerPanel.add( this.candyPanel, BorderLayout.CENTER );
JPanel eastPanel = new JPanel( new BorderLayout() );
JPanel buttonPanel = new JPanel();
buttonPanel.setLayout( new BoxLayout( buttonPanel, BoxLayout.Y_AXIS ) );
this.synthesizeButton = new JButton( "Synthesize!" );
this.synthesizeButton.addActionListener( new SynthesizeListener() );
buttonPanel.add( this.synthesizeButton);
this.automaticButton = new JButton( "Automatic" );
this.automaticButton.addActionListener( new AutomaticListener() );
buttonPanel.add( this.automaticButton);
this.priceCheckButton = new JButton( "Check Prices" );
this.priceCheckButton.addActionListener( new PriceCheckListener() );
buttonPanel.add( this.priceCheckButton);
eastPanel.add( buttonPanel, BorderLayout.NORTH );
this.candyData = new CandyDataPanel();
eastPanel.add( this.candyData, BorderLayout.SOUTH );
this.setLayout( new BorderLayout( 10, 10 ) );
this.add( centerPanel, BorderLayout.CENTER );
this.add( eastPanel, BorderLayout.EAST );
NamedListenerRegistry.registerNamedListener( "(candy)", this );
this.setEnabled( true );
}
private int effectId()
{
return this.effectPanel == null ? -1 : this.effectPanel.currentEffectId();
}
private Candy candy1()
{
return this.candyList1 == null ? null : this.candyList1.currentCandy();
}
private Candy candy2()
{
return this.candyList2 == null ? null : this.candyList2.currentCandy();
}
private static boolean haveSpleenAvailable()
{
boolean loggedIn = KoLCharacter.getUserId() > 0;
return !loggedIn || KoLCharacter.getSpleenLimit() > KoLCharacter.getSpleenUse();
}
@Override
public void setEnabled( final boolean isEnabled )
{
this.synthesizeButton.setEnabled( isEnabled &&
this.effectId() != -1 &&
this.candy1() != null &&
this.candy2() != null &&
SynthesizePanel.haveSpleenAvailable() );
this.automaticButton.setEnabled( isEnabled && this.effectId() != -1 );
this.priceCheckButton.setEnabled( isEnabled && !this.availableChecked );
this.effectPanel.setEnabled( isEnabled );
}
private JPanel addFilters()
{
JPanel filterPanel = new JPanel();
boolean loggedIn = KoLCharacter.getUserId() > 0;
this.availableChecked = loggedIn && !KoLCharacter.canInteract();
this.allowedChecked = loggedIn && KoLCharacter.getRestricted();
this.filters = new JCheckBox[ 2 ];
this.filters[ 0 ] = new JCheckBox( "available", this.availableChecked );
this.filters[ 0 ].setToolTipText( "Show only items that 'acquire' will find. Inventory, at least." );
this.filters[ 1 ] = new JCheckBox( "allowed", this.allowedChecked );
this.filters[ 1 ].setToolTipText( "Show only items that are allowed under Standard restrictions." );
for ( JCheckBox checkbox : this.filters )
{
filterPanel.add( checkbox );
checkbox.addActionListener( this );
}
return filterPanel;
}
// Called when checkbox changes
public void actionPerformed( final ActionEvent e )
{
this.availableChecked = this.filters[0].isSelected();
this.allowedChecked = this.filters[1].isSelected();
// Filter candy lists
this.filterItems();
// Only enable Price Check if you (might be) buying in mall
this.priceCheckButton.setEnabled( !this.availableChecked );
}
// Invoke this in the Swing event thread
private void filterItems()
{
// If we are using available candies only, availability of
// effects might have changed
SynthesizePanel.this.effectPanel.update();
Candy candy1 = this.candy1();
Candy candy2 = this.candy2();
this.candyList1.filterItems( candy1 );
this.candyList2.filterItems( candy2 );
}
// called when (candy) fires
public void update()
{
for ( Candy candy : this.candyList1.getCandyList() )
{
candy.update();
}
for ( Candy candy : this.candyList2.getCandyList() )
{
candy.update();
}
// Having updated the data, update the GUI
try
{
SwingUtilities.invokeAndWait( new Runnable()
{
public void run()
{
SynthesizePanel.this.filterItems();
// Since quantities might have changed, sort
Candy candy1 = SynthesizePanel.this.candy1();
Candy candy2 = SynthesizePanel.this.candy2();
SynthesizePanel.this.candyList1.sortCandy( candy1 );
SynthesizePanel.this.candyList2.sortCandy( candy2 );
SynthesizePanel.this.candyData.update();
}
} );
}
catch ( Exception ie )
{
}
}
private class EffectPanel
extends JPanel
{
public EffectButton selected = null;
public EffectPanel()
{
super( new GridLayout( 3, 5 ) );
// Tier 1 effects
this.add( new EffectButton( "Hot Res +9", EffectPool.SYNTHESIS_HOT ) );
this.add( new EffectButton( "Cold Res +9", EffectPool.SYNTHESIS_COLD ) );
this.add( new EffectButton( "Stench Res +9", EffectPool.SYNTHESIS_PUNGENT ) );
this.add( new EffectButton( "Spooky Res +9", EffectPool.SYNTHESIS_SCARY ) );
this.add( new EffectButton( "Sleaze Res +9", EffectPool.SYNTHESIS_GREASY ) );
// Tier 2 effects
this.add( new EffectButton( "Mus +300%", EffectPool.SYNTHESIS_STRONG ) );
this.add( new EffectButton( "Mys +300%", EffectPool.SYNTHESIS_SMART ) );
this.add( new EffectButton( "Mox +300%", EffectPool.SYNTHESIS_COOL ) );
this.add( new EffectButton( "Max HP +300%", EffectPool.SYNTHESIS_HARDY ) );
this.add( new EffectButton( "Max MP +300%", EffectPool.SYNTHESIS_ENERGY ) );
// Tier 3 effects
this.add( new EffectButton( "Meat +300%", EffectPool.SYNTHESIS_GREED ) );
this.add( new EffectButton( "Item +150%", EffectPool.SYNTHESIS_COLLECTION ) );
this.add( new EffectButton( "Mus Exp +50%", EffectPool.SYNTHESIS_MOVEMENT ) );
this.add( new EffectButton( "Mys Exp +50%", EffectPool.SYNTHESIS_LEARNING ) );
this.add( new EffectButton( "Mox Exp +50%", EffectPool.SYNTHESIS_STYLE ) );
// Wrap the buttons in an attractive titled border
this.setBorder( BorderFactory.createTitledBorder( null, "Effects", TitledBorder.CENTER, TitledBorder.TOP ) );
}
public int currentEffectId()
{
return this.selected == null ? -1 : this.selected.effectId;
}
public void setEnabled( final boolean isEnabled )
{
this.update();
}
public void update()
{
// Enable or disable buttons depending on whether the
// effect can be made using available candies
boolean available = SynthesizePanel.this.availableChecked;
boolean allowed = SynthesizePanel.this.allowedChecked;
int flags = CandyDatabase.makeFlags( available, allowed );
for ( Component component : this.getComponents() )
{
if ( component instanceof EffectButton )
{
EffectButton button = (EffectButton) component;
boolean enabled = !available;
if ( !enabled )
{
int effectId = button.effectId;
int tier = CandyDatabase.getEffectTier( effectId );
for ( int itemId : CandyDatabase.candyForTier( tier, flags ) )
{
if ( CandyDatabase.sweetSynthesisPairing( effectId, itemId, flags ).size() > 0 )
{
enabled = true;
break;
}
}
if ( !enabled && button == this.selected )
{
button.doClick();
}
}
button.setEnabled( enabled );
}
}
}
private class EffectButton
extends JButton
implements ActionListener
{
final public int effectId;
final Color foreground;
final Color background;
public EffectButton( final String name, final int effectId )
{
super( name );
// The following makes the button a solid
// rectangle on OS X.
this.setContentAreaFilled( false );
this.setBorderPainted( false );
this.setOpaque( true );
this.effectId = effectId;
this.foreground = this.getForeground();
this.background = this.getBackground();
this.addActionListener( this );
String effectName = EffectDatabase.getEffectName( effectId );
this.setToolTipText( effectName );
}
private void originalColors()
{
this.setBackground( background );
this.setForeground( foreground );
}
private void reverseColors()
{
this.setBackground( Color.BLACK );
this.setForeground( Color.WHITE );
}
public void actionPerformed( final ActionEvent e )
{
EffectButton current = EffectPanel.this.selected;
if ( current != null )
{
current.originalColors();
SynthesizePanel.this.candyList2.getCandyList().clear();
}
if ( current == this )
{
EffectPanel.this.selected = null;
SynthesizePanel.this.automaticButton.setEnabled( false );
SynthesizePanel.this.candyList1.getCandyList().clear();
}
else
{
EffectPanel.this.selected = this;
SynthesizePanel.this.automaticButton.setEnabled( true );
this.reverseColors();
Set<Integer> candy = CandyDatabase.candyForTier( CandyDatabase.getEffectTier( this.effectId ), 0 );
SynthesizePanel.this.candyList1.loadCandy( candy );
}
}
}
}
// Why can't this be inside CandyTableModel?
private static final String[] columnNames = { "candy", "have", "cost" };
public class CandyTableModel
extends AbstractTableAdapter
{
private static final int NAME = 0;
private static final int COUNT = 1;
private static final int COST = 2;
private final LockableListModel<Candy> model;
public CandyTableModel( LockableListModel<Candy> listModel )
{
super( listModel, columnNames );
this.model = listModel;
}
@Override
public Class<?> getColumnClass( int columnIndex )
{
return this.model.getSize() == 0 ?
Object.class :
this.getValueAt( 0, columnIndex ).getClass();
}
@Override
public Object getValueAt( int rowIndex, int columnIndex )
{
Candy candy = this.model.getElementAt( rowIndex );
switch (columnIndex) {
case NAME:
return candy.getName();
case COUNT:
return IntegerPool.get( candy.getCount() );
case COST:
return IntegerPool.get( candy.getCost() );
default:
throw new IllegalArgumentException( "Invalid column index" );
}
}
}
private class CandyPanel
extends JPanel
{
public CandyPanel()
{
super( new GridLayout( 1, 2 ) );
SynthesizePanel.this.candyList1 = new CandyListA();
this.add( SynthesizePanel.this.candyList1 );
SynthesizePanel.this.candyList2 = new CandyListB();
this.add( SynthesizePanel.this.candyList2 );
}
public abstract class CandyList
extends JPanel
implements ListElementFilter, ListSelectionListener
{
private final CandyTable table;
private final TableRowSorter<CandyTableModel> rowSorter;
private final ListSelectionModel selectionModel;
protected final LockableListModel<Candy> model = new LockableListModel<Candy>();
protected Candy candy = null;
// Don't do anything with ListSelection events while we are sorting the candy list
protected boolean sorting = false;
public CandyList( final String title )
{
super( new BorderLayout() );
this.setPreferredSize( new Dimension( 200, 400 ) );
this.table = new CandyTable( this.model );
this.table.setSelectionMode( ListSelectionModel.SINGLE_SELECTION );
this.selectionModel = this.table.getSelectionModel();
this.selectionModel.addListSelectionListener( this );
this.table.setAutoCreateRowSorter( true );
this.rowSorter = (TableRowSorter<CandyTableModel>)this.table.getRowSorter();
this.rowSorter.setSortable( 0, false );
this.model.setFilter( this );
JScrollPane scrollComponent = new JScrollPane( this.table );
scrollComponent.setBorder( BorderFactory.createTitledBorder( null, title, TitledBorder.CENTER, TitledBorder.TOP ) );
this.add( scrollComponent, BorderLayout.CENTER );
}
public List<Candy> getCandyList()
{
return this.model;
}
public Candy currentCandy()
{
return this.candy;
}
public Candy getSelectedValue()
{
return this.table.getSelectedValue();
}
public abstract void valueChanged( ListSelectionEvent e );
public void loadCandy( Set<Integer> itemIds )
{
List<Candy> list = CandyDatabase.itemIdSetToCandyList( itemIds );
this.model.clear();
this.model.addAll( list );
this.filterItems( null );
this.sortCandy( null );
}
public void sortCandy( final Candy selected )
{
this.sorting = true;
TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>( this.table.getModel() );
this.table.setRowSorter( sorter );
List<RowSorter.SortKey> sortKeys = new ArrayList<RowSorter.SortKey>();
if ( KoLCharacter.canInteract() )
{
// Prefer cheapest candy, then most numerous
sortKeys.add( new RowSorter.SortKey( CandyTableModel.COST, SortOrder.ASCENDING ) );
sortKeys.add( new RowSorter.SortKey( CandyTableModel.COUNT, SortOrder.DESCENDING ) );
}
else
{
// Prefer most numerous candy, then cheapest
sortKeys.add( new RowSorter.SortKey( CandyTableModel.COUNT, SortOrder.DESCENDING ) );
sortKeys.add( new RowSorter.SortKey( CandyTableModel.COST, SortOrder.ASCENDING ) );
}
sortKeys.add( new RowSorter.SortKey( CandyTableModel.NAME, SortOrder.ASCENDING ) );
sorter.setSortKeys( sortKeys );
sorter.sort();
// Don't let user use this TableRowSorter to sort on candy name
sorter.setSortable(0, false);
// Selected item might have moved. Make sure it is visible.
this.selectAndScroll( candy );
this.sorting = false;
}
public void filterItems( final Candy selected )
{
this.model.updateFilter( false );
int size = this.model.getSize();
// Update displayed rows
this.model.fireContentsChanged( this.model, 0, size - 1 );
if ( size == 0 )
{
this.table.clearSelection();
}
else
{
this.selectAndScroll( selected );
}
}
public void selectAndScroll( final Candy selected )
{
if ( selected == null )
{
return;
}
int index = this.model.getIndexOf( selected );
if ( index == -1 )
{
this.table.clearSelection();
return;
}
// We must locate the new index in the sorted view
int size = this.model.getSize();
for ( int i = 0; i < size; ++i )
{
int modelIndex = this.table.convertRowIndexToModel( i );
if ( this.model.getElementAt( modelIndex ).equals( selected ) )
{
this.table.setSelectedIndex( modelIndex );
this.table.ensureIndexIsVisible( modelIndex );
return;
}
}
}
public boolean isVisible( final Object o )
{
if ( o instanceof Candy )
{
if ( SynthesizePanel.this.availableChecked && ((Candy)o).getCount() == 0 )
{
return false;
}
if ( SynthesizePanel.this.allowedChecked && ((Candy)o).getRestricted() )
{
return false;
}
}
return true;
}
public class CandyTable
extends JTable
{
private final LockableListModel<Candy> model;
public CandyTable( LockableListModel<Candy> model )
{
super( new CandyTableModel( model ) );
this.model = model;
// Magic number! Make the name column wide.
this.getColumnModel().getColumn( 0 ).setPreferredWidth( 220 );
}
public void ensureIndexIsVisible( int index )
{
int i = convertRowIndexToView( index );
this.scrollRectToVisible( this.getCellRect( i, 0, true ) );
}
public int getSelectedIndex()
{
int selectedRow = this.getSelectedRow();
return selectedRow == -1 ? -1 : this.convertRowIndexToModel( selectedRow );
}
public Candy getSelectedValue()
{
int selectedRow = this.getSelectedRow();
return selectedRow == -1 ? null : this.model.getElementAt( this.convertRowIndexToModel( selectedRow ) );
}
public void setSelectedIndex( int index )
{
int i = this.convertRowIndexToView( index );
this.setRowSelectionInterval( i, i );
}
}
}
public class CandyListA
extends CandyList
{
public CandyListA()
{
super( "Candy A" );
}
public void valueChanged( ListSelectionEvent e )
{
// The selection is cleared at the beginning of a sort.
// We will restore it when we are done.
if ( this.sorting )
{
return;
}
if ( e.getValueIsAdjusting() )
{
// Mouse down, for example.
// Wait until final event comes in
return;
}
Object item = SynthesizePanel.this.candyList1.getSelectedValue();
Candy current = SynthesizePanel.this.candy1();
Candy replace = (Candy)item;
if ( current != replace )
{
this.candy = replace;
SynthesizePanel.this.candyData.update();
if ( replace == null )
{
SynthesizePanel.this.candyList2.getCandyList().clear();
}
else
{
Set<Integer> candy = CandyDatabase.sweetSynthesisPairing( SynthesizePanel.this.effectId(), replace.getItemId(), 0 );
SynthesizePanel.this.candyList2.loadCandy( candy );
}
SynthesizePanel.this.synthesizeButton.setEnabled( false );
}
return;
}
@Override
public boolean isVisible( final Object o )
{
if ( o instanceof Candy )
{
Candy candy = (Candy)o;
if ( SynthesizePanel.this.availableChecked )
{
// Filter out candy we have none of
int count = candy.getCount();
if ( count == 0 )
{
return false;
}
// Filter out candy which has no available pairing
int effectId = SynthesizePanel.this.effectId();
int itemId = candy.getItemId();
int flags = CandyDatabase.makeFlags( true, SynthesizePanel.this.allowedChecked );
if ( CandyDatabase.sweetSynthesisPairing( effectId, itemId, flags ).size() == 0 )
{
return false;
}
}
if ( SynthesizePanel.this.allowedChecked && candy.getRestricted() )
{
return false;
}
}
return true;
}
}
public class CandyListB
extends CandyList
{
public CandyListB()
{
super( "Candy B" );
}
public void valueChanged( ListSelectionEvent e )
{
// The selection is cleared at the beginning of a sort.
// We will restore it when we are done.
if ( this.sorting )
{
return;
}
if ( e.getValueIsAdjusting() )
{
// Mouse down, for example.
// Wait until final event comes in
return;
}
Object item = SynthesizePanel.this.candyList2.getSelectedValue();
Candy current = SynthesizePanel.this.candy2();
Candy replace = (Candy)item;
if ( current != replace )
{
this.candy = replace;
SynthesizePanel.this.candyData.update();
SynthesizePanel.this.synthesizeButton.setEnabled( replace != null && SynthesizePanel.haveSpleenAvailable() );
}
}
public boolean isVisible( final Object o )
{
if ( o instanceof Candy )
{
Candy candy = (Candy)o;
if ( SynthesizePanel.this.availableChecked )
{
// Filter out candy we have none of.
// You can synthesize two of the same
// candy. If only have one, can't reuse it.
int count = candy.getCount();
if ( ( count == 0 ) ||
( count == 1 && candy.equals( SynthesizePanel.this.candy1() ) ) )
{
return false;
}
}
if ( SynthesizePanel.this.allowedChecked && candy.getRestricted() )
{
return false;
}
}
return true;
}
}
}
private class CandyDataPanel
extends JPanel
{
private final CandyData candyData1;
private final CandyData candyData2;
private final CandyTotal candyTotal;
public CandyDataPanel()
{
super();
this.setLayout( new BoxLayout( this, BoxLayout.Y_AXIS ) );
this.candyData1 = new CandyData( "Candy A" );
this.add( this.candyData1 );
this.candyData2 = new CandyData( "Candy B" );
this.add( this.candyData2 );
this.candyTotal = new CandyTotal();
this.add( this.candyTotal );
}
public void update()
{
Candy candy1 = SynthesizePanel.this.candy1();
Candy candy2 = SynthesizePanel.this.candy2();
this.candyData1.updateCandy( candy1 );
this.candyData2.updateCandy( candy2 );
this.candyTotal.update( candy1, candy2 );
}
private class CandyData
extends JPanel
{
private final JLabel haveValue;
private final JLabel costValue;
public CandyData( final String title )
{
super( new BorderLayout() );
this.setBorder( BorderFactory.createTitledBorder( null, title, TitledBorder.CENTER, TitledBorder.TOP ) );
JPanel labelPanel = new JPanel( new GridLayout( 2, 1 ) );
labelPanel.add( new JLabel( "Have: " ) );
labelPanel.add( new JLabel( "Cost:" ) );
JPanel valuePanel = new JPanel( new GridLayout( 2, 1 ) );
this.haveValue = new JLabel( "" );
valuePanel.add( this.haveValue );
this.costValue = new JLabel( "" );
valuePanel.add( this.costValue );
this.add( labelPanel, BorderLayout.WEST );
this.add( valuePanel, BorderLayout.CENTER );
}
public void updateCandy( Candy candy )
{
if ( candy == null )
{
this.haveValue.setText( "" );
this.costValue.setText( "" );
}
else
{
this.haveValue.setText( String.valueOf( candy.getCount() ) );
this.costValue.setText( String.valueOf( candy.getCost() ) );
}
}
}
private class CandyTotal
extends JPanel
{
private final JLabel totalValue;
private final JLabel perTurnValue;
public CandyTotal()
{
super( new BorderLayout() );
this.setBorder( BorderFactory.createTitledBorder( null, "Total", TitledBorder.CENTER, TitledBorder.TOP ) );
JPanel labelPanel = new JPanel( new GridLayout( 2, 1 ) );
labelPanel.add( new JLabel( "Cost:" ) );
labelPanel.add( new JLabel( "/Adv: " ) );
JPanel valuePanel = new JPanel( new GridLayout( 2, 1 ) );
this.totalValue = new JLabel( "" );
valuePanel.add( this.totalValue );
this.perTurnValue = new JLabel( "" );
valuePanel.add( this.perTurnValue );
this.add( labelPanel, BorderLayout.WEST );
this.add( valuePanel, BorderLayout.CENTER );
}
public void update( Candy candy1, Candy candy2 )
{
if ( candy1 == null || candy2 == null )
{
this.totalValue.setText( "" );
this.perTurnValue.setText( "" );
}
else
{
int total = candy1.getCost() + candy2.getCost();
int perTurn = Math.round( total / 30.0f );
this.totalValue.setText( String.valueOf( total ) );
this.perTurnValue.setText( String.valueOf( perTurn ) );
}
}
}
}
private class SynthesizeListener
extends ThreadedListener
{
@Override
protected void execute()
{
if ( SynthesizePanel.this.candy1() == null || SynthesizePanel.this.candy2() == null || KoLCharacter.getUserId() == 0 )
{
return;
}
Candy candy1 = SynthesizePanel.this.candy1();
int itemId1 = candy1.getItemId();
Candy candy2 = SynthesizePanel.this.candy2();
int itemId2 = candy2.getItemId();
KoLmafia.updateDisplay( "Synthesizing " + candy1 + " with " + candy2 + "..." );
SweetSynthesisRequest request = new SweetSynthesisRequest( itemId1, itemId2 );
RequestThread.postRequest( request );
if ( KoLmafia.permitsContinue() )
{
KoLmafia.updateDisplay( "Done!" );;
}
}
@Override
public String toString()
{
return "synthesize";
}
}
private class AutomaticListener
implements ActionListener
{
public void actionPerformed( final ActionEvent e )
{
int effectId = SynthesizePanel.this.effectId();
if ( effectId == -1 )
{
return;
}
// Flags required by current character state are not
// optional, but the user can use the checkboxes to be
// more restrictive
boolean available = SynthesizePanel.this.availableChecked;
boolean allowed = SynthesizePanel.this.allowedChecked;
int flags = CandyDatabase.defaultFlags() | CandyDatabase.makeFlags( available, allowed );
Candy [] pair = CandyDatabase.synthesisPair( effectId, flags );
if ( pair.length == 0 )
{
KoLmafia.updateDisplay( "Can't find a pair of candies for that effect" );
return;
}
candyList1.selectAndScroll( pair[0] );
candyList2.selectAndScroll( pair[1] );
}
@Override
public String toString()
{
return "automatic";
}
}
private class PriceCheckListener
extends ThreadedListener
{
@Override
protected void execute()
{
for ( Integer itemId : CandyDatabase.candyForTier( 2, 0 ) )
{
if ( !ItemDatabase.isTradeable( itemId ) )
{
continue;
}
float age = MallPriceDatabase.getAge( itemId );
if ( age > SynthesizePanel.AGE_LIMIT )
{
// Force a mall search
StoreManager.getMallPrice( ItemPool.get( itemId, 0 ) );
}
}
// Update all visible candies
SynthesizePanel.this.update();
KoLmafia.updateDisplay( "All prices are less than one hour old." );
}
@Override
public String toString()
{
return "check prices";
}
}
}