Java tutorial
/* * Copyright 2009-2016 Contributors (see credits.txt) * * This file is part of jEveAssets. * * jEveAssets is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * jEveAssets is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with jEveAssets; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ package net.nikr.eve.jeveasset.gui.tabs.tracker; import com.github.lgooddatepicker.optionalusertools.DateChangeListener; import com.github.lgooddatepicker.zinternaltools.DateChangeEvent; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Component; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.geom.Ellipse2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.text.DateFormat; import java.text.DecimalFormat; import java.text.NumberFormat; import java.text.SimpleDateFormat; import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.ZoneId; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; import javax.swing.AbstractListModel; import javax.swing.Icon; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JCheckBoxMenuItem; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JSeparator; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.ToolTipManager; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.event.PopupMenuEvent; import javax.swing.event.PopupMenuListener; import net.nikr.eve.jeveasset.Program; import net.nikr.eve.jeveasset.data.Settings; import net.nikr.eve.jeveasset.gui.images.Images; import net.nikr.eve.jeveasset.gui.shared.Formater; import net.nikr.eve.jeveasset.gui.shared.components.CheckBoxNode; import net.nikr.eve.jeveasset.gui.shared.components.JDateChooser; import net.nikr.eve.jeveasset.gui.shared.components.JDropDownButton; import net.nikr.eve.jeveasset.gui.shared.components.JMainTab; import net.nikr.eve.jeveasset.gui.shared.components.JMultiSelectionList; import net.nikr.eve.jeveasset.gui.shared.menu.JMenuInfo; import net.nikr.eve.jeveasset.gui.tabs.values.Value; import net.nikr.eve.jeveasset.i18n.General; import net.nikr.eve.jeveasset.i18n.TabsTracker; import org.jfree.chart.ChartMouseEvent; import org.jfree.chart.ChartMouseListener; import org.jfree.chart.ChartPanel; import org.jfree.chart.JFreeChart; import org.jfree.chart.axis.DateAxis; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.labels.StandardXYToolTipGenerator; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.renderer.xy.XYItemRenderer; import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; import org.jfree.data.general.DatasetUtilities; import org.jfree.data.time.SimpleTimePeriod; import org.jfree.data.time.TimePeriodValues; import org.jfree.data.time.TimePeriodValuesCollection; import org.jfree.ui.RectangleEdge; public class TrackerTab extends JMainTab { private enum TrackerAction { QUICK_DATE, UPDATE_DATA, UPDATE_SHOWN, INCLUDE_ZERO, ALL, EDIT, DELETE, PROFILE, FILTER_ASSETS, FILTER_WALLET_BALANCE } private final int PANEL_WIDTH = 160; private final int LABEL_WIDTH = 45; private final NumberFormat iskFormat = new DecimalFormat("#,##0.00 isk"); private final DateFormat dateFormat = new SimpleDateFormat(Formater.COLUMN_DATE); private final JFreeChart jNextChart; private final JDateChooser jFrom; private final JDateChooser jTo; private final JMultiSelectionList<String> jOwners; private final JComboBox<QuickDate> jQuickDate; private final JCheckBox jAll; private final JCheckBox jTotal; private final JCheckBox jWalletBalance; private final JButton jWalletBalanceFilters; private final JCheckBox jAssets; private final JButton jAssetsFilters; private final JCheckBox jSellOrders; private final JCheckBox jEscrows; private final JCheckBox jEscrowsToCover; private final JCheckBox jManufacturing; private final JCheckBox jContractCollateral; private final JCheckBox jContractValue; private final JCheckBox jAllProfiles; private final JCheckBoxMenuItem jIncludeZero; private final JPopupMenu jPopupMenu; private final JMenuItem jIskValue; private final JMenuItem jDateValue; private final JTrackerEditDialog jEditDialog; private final JSelectionDialog jSelectionDialog; private final ChartPanel jChartPanel; private final TrackerFilterDialog filterDialog; private final MyRender render; private final Shape NO_FILTER = new Rectangle(-3, -3, 6, 6); private final Shape FILTER_AND_DEFAULT = new Ellipse2D.Float(-3.0f, -3.0f, 6.0f, 6.0f); private final ListenerClass listener = new ListenerClass(); private final TimePeriodValuesCollection dataset = new TimePeriodValuesCollection(); private TimePeriodValues walletBalance; private TimePeriodValues assets; private TimePeriodValues sellOrders; private TimePeriodValues escrows; private TimePeriodValues escrowsToCover; private TimePeriodValues manufacturing; private TimePeriodValues contractCollateral; private TimePeriodValues contractValue; private Map<SimpleTimePeriod, Value> cache; private final Map<String, CheckBoxNode> accountNodes = new TreeMap<String, CheckBoxNode>(); private final Map<String, CheckBoxNode> assetNodes = new TreeMap<String, CheckBoxNode>(); private Integer assetColumn = null; private Integer walletColumn = null; private boolean updateLock = false; public TrackerTab(Program program) { super(program, TabsTracker.get().title(), Images.TOOL_TRACKER.getIcon(), true); filterDialog = new TrackerFilterDialog(program); jPopupMenu = new JPopupMenu(); jPopupMenu.addPopupMenuListener(listener); JMenuItem jMenuItem; jMenuItem = new JMenuItem(TabsTracker.get().edit(), Images.EDIT_EDIT.getIcon()); jMenuItem.setActionCommand(TrackerAction.EDIT.name()); jMenuItem.addActionListener(listener); jPopupMenu.add(jMenuItem); jMenuItem = new JMenuItem(TabsTracker.get().delete(), Images.EDIT_DELETE.getIcon()); jMenuItem.setActionCommand(TrackerAction.DELETE.name()); jMenuItem.addActionListener(listener); jPopupMenu.add(jMenuItem); JMenuInfo.createDefault(jPopupMenu); jIskValue = new JMenuItem(); jIskValue.setEnabled(false); jIskValue.setForeground(Color.BLACK); jIskValue.setHorizontalAlignment(SwingConstants.RIGHT); jIskValue.setDisabledIcon(Images.TOOL_VALUES.getIcon()); jPopupMenu.add(jIskValue); jDateValue = new JMenuItem(); jDateValue.setEnabled(false); jDateValue.setForeground(Color.BLACK); jDateValue.setHorizontalAlignment(SwingConstants.RIGHT); jPopupMenu.add(jDateValue); jEditDialog = new JTrackerEditDialog(program); jSelectionDialog = new JSelectionDialog(program); JSeparator jDateSeparator = new JSeparator(); jQuickDate = new JComboBox<QuickDate>(QuickDate.values()); jQuickDate.setActionCommand(TrackerAction.QUICK_DATE.name()); jQuickDate.addActionListener(listener); JLabel jFromLabel = new JLabel(TabsTracker.get().from()); jFrom = createDateChooser(); JLabel jToLabel = new JLabel(TabsTracker.get().to()); jTo = createDateChooser(); jAll = new JCheckBox(General.get().all()); jAll.setSelected(true); jAll.setActionCommand(TrackerAction.ALL.name()); jAll.addActionListener(listener); jAll.setFont(new Font(jAll.getFont().getName(), Font.ITALIC, jAll.getFont().getSize())); jTotal = new JCheckBox(TabsTracker.get().total()); jTotal.setSelected(true); jTotal.setActionCommand(TrackerAction.UPDATE_SHOWN.name()); jTotal.addActionListener(listener); jWalletBalance = new JCheckBox(TabsTracker.get().walletBalance()); jWalletBalance.setSelected(true); jWalletBalance.setActionCommand(TrackerAction.UPDATE_SHOWN.name()); jWalletBalance.addActionListener(listener); jWalletBalanceFilters = new JButton(Images.LOC_INCLUDE.getIcon()); jWalletBalanceFilters.setActionCommand(TrackerAction.FILTER_WALLET_BALANCE.name()); jWalletBalanceFilters.addActionListener(listener); jAssets = new JCheckBox(TabsTracker.get().assets()); jAssets.setSelected(true); jAssets.setActionCommand(TrackerAction.UPDATE_SHOWN.name()); jAssets.addActionListener(listener); jAssetsFilters = new JButton(Images.LOC_INCLUDE.getIcon()); jAssetsFilters.setActionCommand(TrackerAction.FILTER_ASSETS.name()); jAssetsFilters.addActionListener(listener); jSellOrders = new JCheckBox(TabsTracker.get().sellOrders()); jSellOrders.setSelected(true); jSellOrders.setActionCommand(TrackerAction.UPDATE_SHOWN.name()); jSellOrders.addActionListener(listener); jEscrows = new JCheckBox(TabsTracker.get().escrows()); jEscrows.setSelected(true); jEscrows.setActionCommand(TrackerAction.UPDATE_SHOWN.name()); jEscrows.addActionListener(listener); jEscrowsToCover = new JCheckBox(TabsTracker.get().escrowsToCover()); jEscrowsToCover.setSelected(true); jEscrowsToCover.setActionCommand(TrackerAction.UPDATE_SHOWN.name()); jEscrowsToCover.addActionListener(listener); jManufacturing = new JCheckBox(TabsTracker.get().manufacturing()); jManufacturing.setSelected(true); jManufacturing.setActionCommand(TrackerAction.UPDATE_SHOWN.name()); jManufacturing.addActionListener(listener); jContractCollateral = new JCheckBox(TabsTracker.get().contractCollateral()); jContractCollateral.setSelected(true); jContractCollateral.setActionCommand(TrackerAction.UPDATE_SHOWN.name()); jContractCollateral.addActionListener(listener); jContractValue = new JCheckBox(TabsTracker.get().contractValue()); jContractValue.setSelected(true); jContractValue.setActionCommand(TrackerAction.UPDATE_SHOWN.name()); jContractValue.addActionListener(listener); JSeparator jOwnersSeparator = new JSeparator(); jAllProfiles = new JCheckBox(TabsTracker.get().allProfiles()); jAllProfiles.setActionCommand(TrackerAction.PROFILE.name()); jAllProfiles.addActionListener(listener); jOwners = new JMultiSelectionList<String>(); jOwners.getSelectionModel().addListSelectionListener(listener); JScrollPane jOwnersScroll = new JScrollPane(jOwners); JLabel jHelp = new JLabel(TabsTracker.get().help()); jHelp.setIcon(Images.MISC_HELP.getIcon()); JLabel jNoFilter = new JLabel(TabsTracker.get().helpLegacyData()); jNoFilter.setIcon(new ShapeIcon(NO_FILTER)); JLabel jFilter = new JLabel(TabsTracker.get().helpNewData()); jFilter.setIcon(new ShapeIcon(FILTER_AND_DEFAULT)); JDropDownButton jSettings = new JDropDownButton(Images.DIALOG_SETTINGS.getIcon()); jIncludeZero = new JCheckBoxMenuItem(TabsTracker.get().includeZero()); jIncludeZero.setSelected(true); jIncludeZero.setActionCommand(TrackerAction.INCLUDE_ZERO.name()); jIncludeZero.addActionListener(listener); jSettings.add(jIncludeZero); DateAxis domainAxis = new DateAxis(); domainAxis.setDateFormatOverride(dateFormat); domainAxis.setVerticalTickLabels(true); domainAxis.setAutoTickUnitSelection(true); domainAxis.setAutoRange(true); domainAxis.setTickLabelFont(jFromLabel.getFont()); NumberAxis rangeAxis = new NumberAxis(); rangeAxis.setAutoRange(true); rangeAxis.setStandardTickUnits(NumberAxis.createStandardTickUnits()); rangeAxis.setTickLabelFont(jFromLabel.getFont()); //XYPlot plot = new XYPlot(dataset, domainAxis, rangeAxis, new XYLineAndShapeRenderer(true, true)); render = new MyRender(); XYPlot plot = new XYPlot(dataset, domainAxis, rangeAxis, render); plot.setBackgroundPaint(Color.WHITE); plot.setRangeGridlinePaint(Color.LIGHT_GRAY); plot.setDomainGridlinePaint(Color.LIGHT_GRAY); plot.getRenderer() .setBaseToolTipGenerator(new StandardXYToolTipGenerator("{0}: {2} ({1})", dateFormat, iskFormat)); plot.setDomainCrosshairLockedOnData(true); plot.setDomainCrosshairStroke(new BasicStroke(1)); plot.setDomainCrosshairPaint(Color.BLACK); plot.setDomainCrosshairVisible(false); plot.setRangeCrosshairLockedOnData(true); plot.setRangeCrosshairVisible(false); jNextChart = new JFreeChart(plot); jNextChart.setAntiAlias(true); jNextChart.setBackgroundPaint(jPanel.getBackground()); jNextChart.addProgressListener(null); jNextChart.getLegend().setItemFont(jFrom.getFont()); jChartPanel = new ChartPanel(jNextChart); jChartPanel.addMouseListener(listener); jChartPanel.setDomainZoomable(false); jChartPanel.setRangeZoomable(false); jChartPanel.setPopupMenu(null); jChartPanel.addChartMouseListener(listener); jChartPanel.setMaximumDrawHeight(Integer.MAX_VALUE); jChartPanel.setMaximumDrawWidth(Integer.MAX_VALUE); jChartPanel.setMinimumDrawWidth(10); jChartPanel.setMinimumDrawHeight(10); int AssetsGapWidth = PANEL_WIDTH - jAssets.getPreferredSize().width - jAssetsFilters.getPreferredSize().width; if (AssetsGapWidth < 0) { AssetsGapWidth = 0; } int WalletGapWidth = PANEL_WIDTH - jWalletBalance.getPreferredSize().width - jWalletBalanceFilters.getPreferredSize().width; if (WalletGapWidth < 0) { WalletGapWidth = 0; } layout.setHorizontalGroup(layout.createSequentialGroup().addGroup(layout.createParallelGroup() .addGroup(layout.createSequentialGroup().addComponent(jHelp).addGap(20).addComponent(jNoFilter) .addGap(20).addComponent(jFilter).addGap(20, 20, Integer.MAX_VALUE).addComponent(jSettings) .addGap(6)) .addComponent(jChartPanel)).addGroup( layout.createParallelGroup().addComponent(jQuickDate, PANEL_WIDTH, PANEL_WIDTH, PANEL_WIDTH) .addGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup() .addComponent(jFromLabel, LABEL_WIDTH, LABEL_WIDTH, LABEL_WIDTH) .addComponent(jToLabel, LABEL_WIDTH, LABEL_WIDTH, LABEL_WIDTH)) .addGap(0) .addGroup(layout.createParallelGroup() .addComponent(jFrom, PANEL_WIDTH - LABEL_WIDTH, PANEL_WIDTH - LABEL_WIDTH, PANEL_WIDTH - LABEL_WIDTH) .addComponent(jTo, PANEL_WIDTH - LABEL_WIDTH, PANEL_WIDTH - LABEL_WIDTH, PANEL_WIDTH - LABEL_WIDTH))) .addComponent(jDateSeparator, PANEL_WIDTH, PANEL_WIDTH, PANEL_WIDTH) .addComponent(jAll, PANEL_WIDTH, PANEL_WIDTH, PANEL_WIDTH) .addComponent(jTotal, PANEL_WIDTH, PANEL_WIDTH, PANEL_WIDTH) .addGroup(layout.createSequentialGroup().addComponent(jWalletBalance) .addGap(0, 0, WalletGapWidth).addComponent(jWalletBalanceFilters)) .addGroup(layout.createSequentialGroup().addComponent(jAssets) .addGap(0, 0, AssetsGapWidth).addComponent(jAssetsFilters)) .addComponent(jSellOrders, PANEL_WIDTH, PANEL_WIDTH, PANEL_WIDTH) .addComponent(jEscrows, PANEL_WIDTH, PANEL_WIDTH, PANEL_WIDTH) .addComponent(jEscrowsToCover, PANEL_WIDTH, PANEL_WIDTH, PANEL_WIDTH) .addComponent(jManufacturing, PANEL_WIDTH, PANEL_WIDTH, PANEL_WIDTH) .addComponent(jContractCollateral, PANEL_WIDTH, PANEL_WIDTH, PANEL_WIDTH) .addComponent(jContractValue, PANEL_WIDTH, PANEL_WIDTH, PANEL_WIDTH) .addComponent(jOwnersSeparator, PANEL_WIDTH, PANEL_WIDTH, PANEL_WIDTH) .addComponent(jAllProfiles, PANEL_WIDTH, PANEL_WIDTH, PANEL_WIDTH) .addComponent(jOwnersScroll, PANEL_WIDTH, PANEL_WIDTH, PANEL_WIDTH))); layout.setVerticalGroup(layout.createParallelGroup() .addGroup(layout.createSequentialGroup().addGroup(layout.createParallelGroup() .addComponent(jHelp, Program.getButtonsHeight(), Program.getButtonsHeight(), Program.getButtonsHeight()) .addComponent(jNoFilter, Program.getButtonsHeight(), Program.getButtonsHeight(), Program.getButtonsHeight()) .addComponent(jFilter, Program.getButtonsHeight(), Program.getButtonsHeight(), Program.getButtonsHeight()) .addComponent(jSettings, Program.getButtonsHeight(), Program.getButtonsHeight(), Program.getButtonsHeight())) .addComponent(jChartPanel)) .addGroup(layout.createSequentialGroup() .addComponent(jQuickDate, Program.getButtonsHeight(), Program.getButtonsHeight(), Program.getButtonsHeight()) .addGroup(layout.createParallelGroup() .addComponent(jFromLabel, Program.getButtonsHeight(), Program.getButtonsHeight(), Program.getButtonsHeight()) .addComponent(jFrom, Program.getButtonsHeight(), Program.getButtonsHeight(), Program.getButtonsHeight())) .addGroup(layout.createParallelGroup() .addComponent(jToLabel, Program.getButtonsHeight(), Program.getButtonsHeight(), Program.getButtonsHeight()) .addComponent(jTo, Program.getButtonsHeight(), Program.getButtonsHeight(), Program.getButtonsHeight())) .addComponent(jDateSeparator, 3, 3, 3) .addComponent(jAll, Program.getButtonsHeight(), Program.getButtonsHeight(), Program.getButtonsHeight()) .addComponent(jTotal, Program.getButtonsHeight(), Program.getButtonsHeight(), Program.getButtonsHeight()) .addGroup(layout.createParallelGroup() .addComponent(jWalletBalance, Program.getButtonsHeight(), Program.getButtonsHeight(), Program.getButtonsHeight()) .addComponent(jWalletBalanceFilters, Program.getButtonsHeight(), Program.getButtonsHeight(), Program.getButtonsHeight())) .addGroup(layout.createParallelGroup() .addComponent(jAssets, Program.getButtonsHeight(), Program.getButtonsHeight(), Program.getButtonsHeight()) .addComponent(jAssetsFilters, Program.getButtonsHeight(), Program.getButtonsHeight(), Program.getButtonsHeight())) .addComponent(jSellOrders, Program.getButtonsHeight(), Program.getButtonsHeight(), Program.getButtonsHeight()) .addComponent(jEscrows, Program.getButtonsHeight(), Program.getButtonsHeight(), Program.getButtonsHeight()) .addComponent(jEscrowsToCover, Program.getButtonsHeight(), Program.getButtonsHeight(), Program.getButtonsHeight()) .addComponent(jManufacturing, Program.getButtonsHeight(), Program.getButtonsHeight(), Program.getButtonsHeight()) .addComponent(jContractCollateral, Program.getButtonsHeight(), Program.getButtonsHeight(), Program.getButtonsHeight()) .addComponent(jContractValue, Program.getButtonsHeight(), Program.getButtonsHeight(), Program.getButtonsHeight()) .addComponent(jOwnersSeparator, 3, 3, 3) .addComponent(jAllProfiles, Program.getButtonsHeight(), Program.getButtonsHeight(), Program.getButtonsHeight()) .addComponent(jOwnersScroll, 70, 70, Integer.MAX_VALUE))); } @Override public void updateData() { createNodes(); //Must be first or NPE! updateButtonIcons(); updateOwners(); createData(); updateFilterButtons(); } private JDateChooser createDateChooser() { JDateChooser jDate = new JDateChooser(true); jDate.addDateChangeListener(listener); return jDate; } private void updateFilterButtons() { Set<String> walletIDs = new TreeSet<String>(); Set<String> assetsIDs = new TreeSet<String>(); List<String> owners = jOwners.getSelectedValuesList(); for (String owner : owners) { for (Value data : Settings.get().getTrackerData().get(owner)) { //Get all account wallet account keys for (String id : data.getBalanceFilter().keySet()) { walletIDs.add(id); } //Get all asset IDs for (String id : data.getAssetsFilter().keySet()) { assetsIDs.add(id); } } } jWalletBalanceFilters.setEnabled(!walletIDs.isEmpty()); jAssetsFilters.setEnabled(!assetsIDs.isEmpty()); } private void updateOwners() { updateLock = true; Set<String> owners = new TreeSet<String>(Settings.get().getTrackerData().keySet()); final List<String> ownersList; if (jAllProfiles.isSelected()) { ownersList = new ArrayList<String>(owners); } else { ownersList = new ArrayList<String>(); for (String s : owners) { if (program.getOwnerNames(false).contains(s)) { ownersList.add(s); } } } if (owners.isEmpty()) { jOwners.setEnabled(false); jOwners.setModel(new AbstractListModel<String>() { @Override public int getSize() { return 1; } @Override public String getElementAt(int index) { return TabsTracker.get().noDataFound(); } }); } else { jOwners.setEnabled(true); jOwners.setModel(new AbstractListModel<String>() { @Override public int getSize() { return ownersList.size(); } @Override public String getElementAt(int index) { return ownersList.get(index); } }); jOwners.selectAll(); } updateLock = false; } private void createNodes() { //Find all saved Keys/IDs Set<String> walletIDs = new TreeSet<String>(); Set<String> assetsIDs = new TreeSet<String>(); for (List<Value> values : Settings.get().getTrackerData().values()) { for (Value data : values) { //Get all account wallet account keys walletIDs.addAll(data.getBalanceFilter().keySet()); //Get all asset IDs assetsIDs.addAll(data.getAssetsFilter().keySet()); } } //WALLET - Make nodes for found wallet account keys CheckBoxNode corporationWalletNode = new CheckBoxNode(null, TabsTracker.get().corporationWallet(), TabsTracker.get().corporationWallet(), false); for (String id : walletIDs) { accountNodes.put(id, new CheckBoxNode(corporationWalletNode, id, TabsTracker.get().division(id), selectNode(id))); } String charecterWalletID = "0"; CheckBoxNode charecterWalletNode = new CheckBoxNode(null, charecterWalletID, TabsTracker.get().characterWallet(), selectNode(charecterWalletID)); accountNodes.put(charecterWalletID, charecterWalletNode); //ASSETS - Make nodes for found asset IDs CheckBoxNode assetNode = new CheckBoxNode(null, TabsTracker.get().assets(), TabsTracker.get().assets(), false); Map<String, CheckBoxNode> nodeCache = new HashMap<String, CheckBoxNode>(); for (String id : assetsIDs) { String[] ids = id.split(" > "); String location; String flag; if (ids.length == 2) { //Location location = ids[0]; //Flag flag = ids[1]; } else { location = id; flag = null; //Never used } CheckBoxNode locationNode = nodeCache.get(location); if (locationNode == null) { locationNode = new CheckBoxNode(assetNode, location, location, selectNode(location)); nodeCache.put(location, locationNode); } CheckBoxNode flagNode = nodeCache.get(id); if (flagNode == null) { flagNode = new CheckBoxNode(locationNode, id, flag, selectNode(id)); nodeCache.put(id, flagNode); } assetNodes.put(id, flagNode); } //For locations with office, you should have the option to exclude all values in corp hangars for (CheckBoxNode locationNode : nodeCache.values()) { if (locationNode.isParent()) { String id = locationNode.getNodeId() + " > unique ID"; CheckBoxNode otherNode = new CheckBoxNode(locationNode, id, TabsTracker.get().other(), selectNode(id)); assetNodes.put(locationNode.getNodeId(), otherNode); } } } private boolean selectNode(String id) { Boolean selected = Settings.get().getTrackerFilters().get(id); if (selected != null) { return selected; } else { return Settings.get().isTrackerSelectNew(); } } private void createData() { if (updateLock) { return; } List<String> owners = jOwners.getSelectedValuesList(); walletBalance = new TimePeriodValues(TabsTracker.get().walletBalance()); assets = new TimePeriodValues(TabsTracker.get().assets()); sellOrders = new TimePeriodValues(TabsTracker.get().sellOrders()); escrows = new TimePeriodValues(TabsTracker.get().escrows()); escrowsToCover = new TimePeriodValues(TabsTracker.get().escrowsToCover()); manufacturing = new TimePeriodValues(TabsTracker.get().manufacturing()); contractCollateral = new TimePeriodValues(TabsTracker.get().contractCollateral()); contractValue = new TimePeriodValues(TabsTracker.get().contractValue()); Date from = getFromDate(); Date to = getToDate(); cache = new TreeMap<SimpleTimePeriod, Value>(); Map<Date, Boolean> assetColumns = new TreeMap<Date, Boolean>(); Map<Date, Boolean> walletColumns = new TreeMap<Date, Boolean>(); if (owners != null) { //No data set... for (String owner : owners) { for (Value data : Settings.get().getTrackerData().get(owner)) { SimpleTimePeriod date = new SimpleTimePeriod(data.getDate(), data.getDate()); if ((from == null || data.getDate().after(from)) && (to == null || data.getDate().before(to))) { Value value = cache.get(date); if (value == null) { value = new Value(data.getDate()); cache.put(date, value); } //Default Boolean assetBoolean = assetColumns.get(data.getDate()); if (assetBoolean == null) { assetColumns.put(data.getDate(), false); } Boolean walletBoolean = walletColumns.get(data.getDate()); if (walletBoolean == null) { walletColumns.put(data.getDate(), false); } if (data.getAssetsFilter().isEmpty()) { value.addAssets(data.getAssetsTotal()); } else { assetColumns.put(data.getDate(), true); for (Map.Entry<String, Double> entry : data.getAssetsFilter().entrySet()) { if (assetNodes.get(entry.getKey()).isSelected()) { value.addAssets(entry.getKey(), entry.getValue()); } } } value.addEscrows(data.getEscrows()); value.addEscrowsToCover(data.getEscrowsToCover()); value.addManufacturing(data.getManufacturing()); value.addContractCollateral(data.getContractCollateral()); value.addContractValue(data.getContractValue()); value.addSellOrders(data.getSellOrders()); if (data.getBalanceFilter().isEmpty()) { value.addBalance(data.getBalanceTotal()); } else { walletColumns.put(data.getDate(), true); for (Map.Entry<String, Double> entry : data.getBalanceFilter().entrySet()) { if (accountNodes.get(entry.getKey()).isSelected()) { value.addBalance(entry.getKey(), entry.getValue()); } } } } } } for (Map.Entry<SimpleTimePeriod, Value> entry : cache.entrySet()) { walletBalance.add(entry.getKey(), entry.getValue().getBalanceTotal()); assets.add(entry.getKey(), entry.getValue().getAssetsTotal()); sellOrders.add(entry.getKey(), entry.getValue().getSellOrders()); escrows.add(entry.getKey(), entry.getValue().getEscrows()); escrowsToCover.add(entry.getKey(), entry.getValue().getEscrowsToCover()); manufacturing.add(entry.getKey(), entry.getValue().getManufacturing()); contractCollateral.add(entry.getKey(), entry.getValue().getContractCollateral()); contractValue.add(entry.getKey(), entry.getValue().getContractValue()); } } int count; count = 0; assetColumn = assetColumns.size(); //Default for (Map.Entry<Date, Boolean> entry : assetColumns.entrySet()) { if (entry.getValue()) { assetColumn = count; break; } count++; } count = 0; walletColumn = walletColumns.size(); //Default for (Map.Entry<Date, Boolean> entry : walletColumns.entrySet()) { if (entry.getValue()) { walletColumn = count; break; } count++; } updateShown(); } private void updateButtonIcons() { boolean isAll; boolean isSome; //Assets isAll = true; isSome = false; for (CheckBoxNode node : assetNodes.values()) { if (!node.isSelected()) { isAll = false; } if (node.isSelected()) { isSome = true; } } if (isAll) { jAssetsFilters.setIcon(Images.LOC_INCLUDE.getIcon()); } else if (isSome) { jAssetsFilters.setIcon(Images.EDIT_EDIT_WHITE.getIcon()); } else { jAssetsFilters.setIcon(Images.UPDATE_DONE_ERROR.getIcon()); } //Wallet isAll = true; isSome = false; for (CheckBoxNode node : accountNodes.values()) { if (!node.isSelected()) { isAll = false; } if (node.isSelected()) { isSome = true; } } if (isAll) { jWalletBalanceFilters.setIcon(Images.LOC_INCLUDE.getIcon()); } else if (isSome) { jWalletBalanceFilters.setIcon(Images.EDIT_EDIT_WHITE.getIcon()); } else { jWalletBalanceFilters.setIcon(Images.UPDATE_DONE_ERROR.getIcon()); } } private void updateShown() { //Remove All while (dataset.getSeriesCount() != 0) { dataset.removeSeries(0); } render.clear(); if (jTotal.isSelected()) { //Update total TimePeriodValues total = new TimePeriodValues(TabsTracker.get().total()); for (Map.Entry<SimpleTimePeriod, Value> entry : cache.entrySet()) { double t = 0; if (jWalletBalance.isSelected() && walletBalance != null) { t += entry.getValue().getBalanceTotal(); } if (jAssets.isSelected() && assets != null) { t += entry.getValue().getAssetsTotal(); } if (jSellOrders.isSelected() && sellOrders != null) { t += entry.getValue().getSellOrders(); } if (jEscrows.isSelected() && escrows != null) { t += entry.getValue().getEscrows(); } //Escrows To Cover is not money you own, It's technically money you owe //Therefor it's not included in the total //See: https://forums.eveonline.com/default.aspx?g=posts&m=6607898#post6607898 //if (jEscrowsToCover.isSelected() && escrowsToCover != null) { // t += entry.getValue().getEscrowsToCover(); //} if (jManufacturing.isSelected() && manufacturing != null) { t += entry.getValue().getManufacturing(); } if (jContractCollateral.isSelected() && contractCollateral != null) { t += entry.getValue().getContractCollateral(); } if (jContractValue.isSelected() && contractValue != null) { t += entry.getValue().getContractValue(); } total.add(entry.getKey(), t); } dataset.addSeries(total); Integer minColumn = null; if (jWalletBalance.isSelected() && walletColumn != null) { minColumn = walletColumn; } if (jAssets.isSelected() && assetColumn != null) { if (minColumn != null) { minColumn = Math.min(minColumn, assetColumn); } else { minColumn = assetColumn; } } render.add(dataset.getSeriesCount() - 1, minColumn); updateRender(dataset.getSeriesCount() - 1, Color.RED.darker()); } if (jWalletBalance.isSelected() && walletBalance != null) { dataset.addSeries(walletBalance); render.add(dataset.getSeriesCount() - 1, walletColumn); updateRender(dataset.getSeriesCount() - 1, Color.BLUE.darker()); } if (jAssets.isSelected() && assets != null) { dataset.addSeries(assets); render.add(dataset.getSeriesCount() - 1, assetColumn); updateRender(dataset.getSeriesCount() - 1, Color.GREEN.darker().darker()); } if (jSellOrders.isSelected() && sellOrders != null) { dataset.addSeries(sellOrders); updateRender(dataset.getSeriesCount() - 1, Color.CYAN.darker()); } if (jEscrows.isSelected() && escrows != null) { dataset.addSeries(escrows); updateRender(dataset.getSeriesCount() - 1, Color.BLACK); } if (jEscrowsToCover.isSelected() && escrowsToCover != null) { dataset.addSeries(escrowsToCover); updateRender(dataset.getSeriesCount() - 1, Color.GRAY); } if (jManufacturing.isSelected() && manufacturing != null) { dataset.addSeries(manufacturing); updateRender(dataset.getSeriesCount() - 1, Color.MAGENTA); } if (jContractCollateral.isSelected() && contractCollateral != null) { dataset.addSeries(contractCollateral); updateRender(dataset.getSeriesCount() - 1, Color.PINK); } if (jContractValue.isSelected() && contractValue != null) { dataset.addSeries(contractValue); updateRender(dataset.getSeriesCount() - 1, Color.ORANGE); } //Add empty dataset if (dataset.getSeriesCount() == 0) { TimePeriodValues timePeriodValues = new TimePeriodValues(TabsTracker.get().empty()); dataset.addSeries(timePeriodValues); updateRender(dataset.getSeriesCount() - 1, Color.BLACK); } jNextChart.getXYPlot().getRangeAxis().setAutoRange(true); jNextChart.getXYPlot().getDomainAxis().setAutoRange(true); Number maxNumber = DatasetUtilities.findMaximumRangeValue(dataset); NumberAxis rangeAxis = (NumberAxis) jNextChart.getXYPlot().getRangeAxis(); rangeAxis.setNumberFormatOverride(Formater.LONG_FORMAT); //Default if (maxNumber != null && (maxNumber instanceof Double)) { double max = (Double) maxNumber; if (max > 1000000000000.0) { //Higher than 1 Trillion rangeAxis.setNumberFormatOverride(Formater.TRILLIONS_FORMAT); } else if (max > 1000000000.0) { //Higher than 1 Billion rangeAxis.setNumberFormatOverride(Formater.BILLIONS_FORMAT); } else if (max > 1000000.0) { //Higher than 1 Million rangeAxis.setNumberFormatOverride(Formater.MILLIONS_FORMAT); } } } private void updateRender(int index, Color color) { XYItemRenderer renderer = jNextChart.getXYPlot().getRenderer(); renderer.setSeriesPaint(index, color); renderer.setSeriesStroke(index, new BasicStroke(1)); } private void updateSettings() { Settings.lock("Tracker Filters: Update"); Settings.get().getTrackerFilters().clear(); for (CheckBoxNode checkBoxNode : assetNodes.values()) { Settings.get().getTrackerFilters().put(checkBoxNode.getNodeId(), checkBoxNode.isSelected()); } for (CheckBoxNode checkBoxNode : accountNodes.values()) { Settings.get().getTrackerFilters().put(checkBoxNode.getNodeId(), checkBoxNode.isSelected()); } Settings.get().setTrackerSelectNew(filterDialog.isSelectNew()); Settings.unlock("Tracker Filters: Update"); Settings.saveSettings(); } private String getSelectedOwner(boolean all) { if (jOwners.getSelectedIndices().length == 1) { return jOwners.getSelectedValue(); } else { List<String> owners = jOwners.getSelectedValuesList(); List<String> list = new ArrayList<String>(); if (all) { list.add(General.get().all()); } for (String owner : owners) { Value value = getSelectedValue(owner); if (value != null) { list.add(owner); } } return jSelectionDialog.showOwner(list); } } private Value getSelectedValue(String owner) { String date = Formater.simpleDate(new Date((long) jNextChart.getXYPlot().getDomainCrosshairValue())); for (Value value : Settings.get().getTrackerData().get(owner)) { if (date.equals(Formater.simpleDate(value.getDate()))) { return value; } } return null; } private class MyRender extends XYLineAndShapeRenderer { Map<Integer, Integer> renders = new HashMap<Integer, Integer>(); public MyRender() { super(true, true); } @Override public Shape getItemShape(int row, int column) { Integer findColumn = renders.get(row); if (findColumn != null && findColumn > column) { return NO_FILTER; } else { return FILTER_AND_DEFAULT; } } public void clear() { renders.clear(); } public void add(int row, Integer column) { if (column != null) { renders.put(row, column); } } } private static class ShapeIcon implements Icon { private final Shape shape; public ShapeIcon(Shape shape) { this.shape = shape; } @Override public void paintIcon(Component c, Graphics g, int x, int y) { Graphics2D g2d = (Graphics2D) g.create(); RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); rh.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); g2d.setRenderingHints(rh); g2d.setColor(Color.BLACK); g2d.translate(x - shape.getBounds().x, y - shape.getBounds().y); g2d.fill(shape); g2d.dispose(); } @Override public int getIconWidth() { return shape.getBounds().width; } @Override public int getIconHeight() { return shape.getBounds().height; } } private Date getFromDate() { LocalDate date = jFrom.getDate(); if (date == null) { return null; } Instant instant = date.atStartOfDay().atZone(ZoneId.of("GMT")).toInstant(); //Start of day - GMT return Date.from(instant); } private Date getToDate() { LocalDate date = jTo.getDate(); if (date == null) { return null; } Instant instant = date.atTime(23, 59, 59).atZone(ZoneId.of("GMT")).toInstant(); //End of day - GMT return Date.from(instant); } private LocalDate dateToLocalDate(Date date) { Instant instant = date.toInstant(); return LocalDateTime.ofInstant(instant, ZoneId.of("GMT")).toLocalDate(); } private class ListenerClass extends MouseAdapter implements ActionListener, PopupMenuListener, ChartMouseListener, ListSelectionListener, DateChangeListener { private int defaultDismissTimeout; private int defaultInitialDelay; @Override public void mouseEntered(MouseEvent me) { defaultDismissTimeout = ToolTipManager.sharedInstance().getDismissDelay(); defaultInitialDelay = ToolTipManager.sharedInstance().getInitialDelay(); ToolTipManager.sharedInstance().setDismissDelay(60000); ToolTipManager.sharedInstance().setInitialDelay(0); } @Override public void mouseExited(MouseEvent me) { ToolTipManager.sharedInstance().setDismissDelay(defaultDismissTimeout); ToolTipManager.sharedInstance().setInitialDelay(defaultInitialDelay); } @Override public void actionPerformed(ActionEvent e) { if (TrackerAction.QUICK_DATE.name().equals(e.getActionCommand())) { QuickDate quickDate = (QuickDate) jQuickDate.getSelectedItem(); if (quickDate == QuickDate.RESET) { jTo.setDate(null); jFrom.setDate(null); } else { Date toDate = getToDate(); if (toDate == null) { toDate = new Date(); //now } Date fromDate = quickDate.apply(toDate); if (fromDate != null) { jFrom.setDate(dateToLocalDate(fromDate)); } } } else if (TrackerAction.INCLUDE_ZERO.name().equals(e.getActionCommand())) { NumberAxis domainAxis = (NumberAxis) jNextChart.getXYPlot().getRangeAxis(); domainAxis.setAutoRangeIncludesZero(jIncludeZero.isSelected()); } else if (TrackerAction.UPDATE_DATA.name().equals(e.getActionCommand())) { createData(); } else if (TrackerAction.UPDATE_SHOWN.name().equals(e.getActionCommand())) { updateShown(); jAll.setSelected(jTotal.isSelected() && jWalletBalance.isSelected() && jAssets.isSelected() && jSellOrders.isSelected() && jEscrows.isSelected() && jEscrowsToCover.isSelected() && jManufacturing.isSelected() && jContractCollateral.isSelected() && jContractValue.isSelected()); } else if (TrackerAction.ALL.name().equals(e.getActionCommand())) { jTotal.setSelected(jAll.isSelected()); jWalletBalance.setSelected(jAll.isSelected()); jAssets.setSelected(jAll.isSelected()); jSellOrders.setSelected(jAll.isSelected()); jEscrows.setSelected(jAll.isSelected()); jEscrowsToCover.setSelected(jAll.isSelected()); jManufacturing.setSelected(jAll.isSelected()); jContractCollateral.setSelected(jAll.isSelected()); jContractValue.setSelected(jAll.isSelected()); updateShown(); } else if (TrackerAction.EDIT.name().equals(e.getActionCommand())) { jNextChart.getXYPlot().setDomainCrosshairVisible(true); String owner = getSelectedOwner(false); if (owner == null) { return; } Value value = getSelectedValue(owner); if (value == null) { return; } boolean update = jEditDialog.showEdit(value); if (update) { createData(); } jNextChart.getXYPlot().setDomainCrosshairVisible(false); } else if (TrackerAction.DELETE.name().equals(e.getActionCommand())) { jNextChart.getXYPlot().setDomainCrosshairVisible(true); String owner = getSelectedOwner(true); if (owner == null) { return; } List<String> owners = new ArrayList<String>(); if (owner.equals(General.get().all())) { owners.addAll(jOwners.getSelectedValuesList()); } else { owners.add(owner); } Map<String, Value> values = new HashMap<String, Value>(); for (String s : owners) { Value value = getSelectedValue(s); if (value != null) { values.put(s, value); } } if (values.isEmpty()) { return; } int retrunValue = JOptionPane.showConfirmDialog(program.getMainWindow().getFrame(), TabsTracker.get().deleteSelected(), TabsTracker.get().delete(), JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE); if (retrunValue == JOptionPane.OK_OPTION) { Settings.lock("Tracker Data (Delete)"); for (Map.Entry<String, Value> entry : values.entrySet()) { //Remove value Settings.get().getTrackerData().get(entry.getKey()).remove(entry.getValue()); //Remove empty owner if (Settings.get().getTrackerData().get(entry.getKey()).isEmpty()) { Settings.get().getTrackerData().remove(entry.getKey()); updateOwners(); } } Settings.unlock("Tracker Data (Delete)"); program.saveSettings("Tracker Data (Delete)"); createData(); updateFilterButtons(); } jNextChart.getXYPlot().setDomainCrosshairVisible(false); } else if (TrackerAction.PROFILE.name().equals(e.getActionCommand())) { updateOwners(); updateData(); } else if (TrackerAction.FILTER_WALLET_BALANCE.name().equals(e.getActionCommand())) { boolean save = filterDialog.showWallet(accountNodes); if (save) { //Need refilter updateSettings(); createData(); updateButtonIcons(); } } else if (TrackerAction.FILTER_ASSETS.name().equals(e.getActionCommand())) { boolean save = filterDialog.showLocations(assetNodes); if (save) { //Need refilter updateSettings(); createData(); updateButtonIcons(); } } } @Override public void dateChanged(DateChangeEvent event) { Date from = getFromDate(); Date to = getToDate(); QuickDate quickDate = (QuickDate) jQuickDate.getSelectedItem(); if (!quickDate.isValid(from, to)) { QuickDate selected = quickDate.getSelected(from, to); jQuickDate.setSelectedItem(selected); } if (from != null && to != null && from.after(to)) { jTo.setDate(dateToLocalDate(from)); } createData(); } boolean mouseClicked = false; @Override public void chartMouseClicked(final ChartMouseEvent cme) { if (cme.getTrigger().getClickCount() % 2 == 0) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { if (cache.isEmpty()) { return; } jNextChart.getXYPlot().setDomainCrosshairVisible(true); Point2D p = jChartPanel.translateScreenToJava2D(cme.getTrigger().getPoint()); double xValue = jNextChart.getXYPlot().getDomainCrosshairValue(); double yValue = jNextChart.getXYPlot().getRangeCrosshairValue(); RectangleEdge xEdge = jNextChart.getXYPlot().getDomainAxisEdge(); RectangleEdge yEdge = jNextChart.getXYPlot().getRangeAxisEdge(); Rectangle2D dataArea = jChartPanel.getScreenDataArea(); // jChartPanel.getChartRenderingInfo().getPlotInfo().getSubplotInfo(0).getDataArea(); int x = (int) jNextChart.getXYPlot().getDomainAxis().valueToJava2D(xValue, dataArea, xEdge); int y = (int) jNextChart.getXYPlot().getRangeAxis().valueToJava2D(yValue, dataArea, yEdge); jIskValue.setText(iskFormat.format(yValue)); jDateValue.setText(dateFormat.format(new Date((long) xValue))); jPopupMenu.show((Component) cme.getTrigger().getSource(), x, y); } }); } } @Override public void chartMouseMoved(ChartMouseEvent cme) { } @Override public void popupMenuWillBecomeVisible(PopupMenuEvent e) { } @Override public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { jNextChart.getXYPlot().setDomainCrosshairVisible(false); } @Override public void popupMenuCanceled(PopupMenuEvent e) { } @Override public void valueChanged(ListSelectionEvent e) { updateFilterButtons(); createData(); } } }