/* * Copyright 2009-2020 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.shared.menu; import java.awt.Component; import java.awt.Point; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.util.Arrays; import java.util.EnumMap; import java.util.HashMap; import java.util.Map; import javax.swing.JComponent; import javax.swing.JMenu; import javax.swing.JPopupMenu; import javax.swing.JTable; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import net.nikr.eve.jeveasset.Program; import net.nikr.eve.jeveasset.data.api.my.MyAsset; import net.nikr.eve.jeveasset.data.sde.Item; import net.nikr.eve.jeveasset.data.settings.types.ItemType; import net.nikr.eve.jeveasset.data.settings.types.JumpType; import net.nikr.eve.jeveasset.data.settings.types.LocationType; import net.nikr.eve.jeveasset.data.settings.types.LocationsType; import net.nikr.eve.jeveasset.data.settings.types.PriceType; import net.nikr.eve.jeveasset.data.settings.types.TagsType; import net.nikr.eve.jeveasset.gui.shared.components.JDropDownButton; import net.nikr.eve.jeveasset.gui.tabs.stockpile.Stockpile; import net.nikr.eve.jeveasset.gui.tabs.tree.TreeAsset; public class MenuManager<Q> { public enum MenuEnum { ASSET_FILTER, TAGS, STOCKPILE, LOOKUP, LOCATION, UI, PRICE, NAME, REPROCESSED, JUMPS, ROUTING, COPY_PLUS, SUM, } private boolean priceSupported = false; private boolean itemSupported = false; private boolean locationSupported = false; private boolean jumpsSupported = false; private boolean assets = false; private boolean stockpile = false; private boolean tagsSupported = false; private final Map<MenuEnum, AutoMenu<Q>> mainMenu = new EnumMap<>(MenuEnum.class); private final Map<MenuEnum, AutoMenu<Q>> tablePopupMenu = new EnumMap<>(MenuEnum.class); private final TableMenu<Q> tableMenu; private final JTable jTable; private final Program program; private static final Map<Class<?>, MenuManager<?>> MANAGERS = new HashMap<Class<?>, MenuManager<?>>(); public static <Q> void install(final Program program, final TableMenu<Q> tableMenu, final JTable jTable, final Class<Q> clazz) { MenuManager<Q> menuManager = new MenuManager<>(program, tableMenu, jTable, clazz); MenuManager<?> put = MANAGERS.put(clazz, menuManager); if (put != null) { throw new RuntimeException("Duplicated MenuManager Class"); } } public static void update(final Program program, final Class<?> clazz) { MenuManager<?> menuManager = MANAGERS.get(clazz); if (menuManager != null) { menuManager.updateMainTableMenu(); } else { //No table menu JMenu jMenu = program.getMainWindow().getMenu().getTableMenu(); jMenu.removeAll(); jMenu.setEnabled(false); } } private MenuManager(final Program program, final TableMenu<Q> tableMenu, final JTable jTable, final Class<Q> clazz) { this.program = program; this.tableMenu = tableMenu; this.jTable = jTable; assets = MyAsset.class.isAssignableFrom(clazz) && !TreeAsset.class.isAssignableFrom(clazz); stockpile = Stockpile.StockpileItem.class.isAssignableFrom(clazz); locationSupported = LocationType.class.isAssignableFrom(clazz) || LocationsType.class.isAssignableFrom(clazz); jumpsSupported = JumpType.class.isAssignableFrom(clazz); itemSupported = ItemType.class.isAssignableFrom(clazz); tagsSupported = TagsType.class.isAssignableFrom(clazz); priceSupported = PriceType.class.isAssignableFrom(clazz) || Item.class.isAssignableFrom(clazz); createCashe(program, mainMenu, clazz); createCashe(program, tablePopupMenu, clazz); ListenerClass listener = new ListenerClass(); jTable.addMouseListener(listener); jTable.getTableHeader().addMouseListener(listener); jTable.getSelectionModel().addListSelectionListener(listener); jTable.getColumnModel().getSelectionModel().addListSelectionListener(listener); updateMainTableMenu(); } public final void createCashe(final Program program, final Map<MenuEnum, AutoMenu<Q>> menus, final Class<Q> clazz) { //COPY SIMPLE if (itemSupported) { menus.put(MenuEnum.COPY_PLUS, new JMenuCopyPlus<>(program)); } //ASSET FILTER if (!assets && (itemSupported || locationSupported)) { menus.put(MenuEnum.ASSET_FILTER, new JMenuAssetFilter<>(program)); } //STOCKPILE (Add To) if (!stockpile && itemSupported) { menus.put(MenuEnum.STOCKPILE, new JMenuStockpile<>(program)); } //LOOKUP if (itemSupported || locationSupported) { menus.put(MenuEnum.LOOKUP, new JMenuLookup<>(program)); } //EDIT if (priceSupported) { menus.put(MenuEnum.PRICE, new JMenuPrice<>(program)); } if (assets) { menus.put(MenuEnum.NAME, new JMenuName<>(program)); } if (tagsSupported) { menus.put(MenuEnum.TAGS, new JMenuTags<>(program)); } //REPROCESSED if (itemSupported) { menus.put(MenuEnum.REPROCESSED, new JMenuReprocessed<>(program)); } //JUMPS if (jumpsSupported) { menus.put(MenuEnum.JUMPS, new JMenuJumps<>(program, clazz)); } if (locationSupported) { menus.put(MenuEnum.ROUTING, new JMenuRouting<>(program)); } //LOCATION if (locationSupported) { menus.put(MenuEnum.LOCATION, new JMenuLocation<>(program)); } if (locationSupported || priceSupported || itemSupported) { menus.put(MenuEnum.UI, new JMenuUI<>(program)); } //SUM menus.put(MenuEnum.SUM, new JMenuSum<>(program, jTable)); } private void createMenu(JPopupMenu jPopupMenu) { createMenu(jPopupMenu, tablePopupMenu); } public void createMenu(JMenu jMenu) { createMenu(jMenu, mainMenu); } private void createMenu(JComponent jComponent, Map<MenuEnum, AutoMenu<Q>> menus) { jComponent.removeAll(); boolean notEmpty = false; //UPDATE MenuData<Q> menuData = tableMenu.getMenuData(); for (AutoMenu<Q> jAutoMenu : menus.values()) { jAutoMenu.setMenuData(menuData); } //COPY if (jComponent instanceof JPopupMenu) { jComponent.add(new JMenuCopy(jTable)); addSeparator(jComponent); notEmpty = true; } //TOOL MENU tableMenu.addToolMenu(jComponent); //COPY+ AutoMenu<Q> jcopyPlus = menus.get(MenuEnum.COPY_PLUS); if (jcopyPlus != null) { jComponent.add(jcopyPlus.getComponent()); notEmpty = true; } //FILTER JMenu filterMenu = tableMenu.getFilterMenu(); if (filterMenu != null) { jComponent.add(filterMenu); notEmpty = true; } //ASSET FILTER AutoMenu<Q> jAssetFilter = menus.get(MenuEnum.ASSET_FILTER); if (jAssetFilter != null) { jComponent.add(jAssetFilter.getComponent()); notEmpty = true; if (jAssetFilter instanceof JMenuAssetFilter) { JMenuAssetFilter<?> jMenuAssetFilter = (JMenuAssetFilter) jAssetFilter; jMenuAssetFilter.setTool(tableMenu); } } //STOCKPILE (Add To) AutoMenu<Q> jStockpile = menus.get(MenuEnum.STOCKPILE); if (jStockpile != null) { jComponent.add(jStockpile.getComponent()); notEmpty = true; } //LOOKUP AutoMenu<Q> jLookup = menus.get(MenuEnum.LOOKUP); if (jLookup != null) { jComponent.add(jLookup.getComponent()); notEmpty = true; if (jLookup instanceof JMenuLookup) { JMenuLookup<?> jMenuLookup = (JMenuLookup) jLookup; jMenuLookup.setTool(tableMenu); } } //EDIT AutoMenu<Q> jPrice = menus.get(MenuEnum.PRICE); if (jPrice != null) { jComponent.add(jPrice.getComponent()); notEmpty = true; } AutoMenu<Q> jName = menus.get(MenuEnum.NAME); if (jName != null) { jComponent.add(jName.getComponent()); notEmpty = true; } AutoMenu<Q> jTags = menus.get(MenuEnum.TAGS); if (jTags != null) { jComponent.add(jTags.getComponent()); notEmpty = true; } //REPROCESSED AutoMenu<Q> jReprocessed = menus.get(MenuEnum.REPROCESSED); if (jReprocessed != null) { jComponent.add(jReprocessed.getComponent()); notEmpty = true; } //JUMPS AutoMenu<Q> jJumps = menus.get(MenuEnum.JUMPS); if (jJumps != null) { jComponent.add(jJumps.getComponent()); notEmpty = true; } AutoMenu<Q> jRouting = menus.get(MenuEnum.ROUTING); if (jRouting != null) { jComponent.add(jRouting.getComponent()); notEmpty = true; } //LOCATION AutoMenu<Q> jLocation = menus.get(MenuEnum.LOCATION); if (jLocation != null) { jComponent.add(jLocation.getComponent()); notEmpty = true; } AutoMenu<Q> jUi = menus.get(MenuEnum.UI); if (jUi != null) { jComponent.add(jUi.getComponent()); notEmpty = true; } //COLUMNS JMenu columnMenu = tableMenu.getColumnMenu(); if (columnMenu != null) { jComponent.add(columnMenu); notEmpty = true; } //INFO if (assets) { JMenuInfo.asset(jTable, menuData.getAssets()); } tableMenu.addInfoMenu(jComponent); //SUM AutoMenu<Q> jSum = menus.get(MenuEnum.SUM); if (jSum != null) { jComponent.add(jSum.getComponent()); notEmpty = true; } jComponent.setEnabled(notEmpty); } private void updateMainTableMenu() { createMenu(program.getMainWindow().getMenu().getTableMenu()); } private void showTableHeaderPopupMenu(final MouseEvent e) { JPopupMenu jTableHeaderPopupMenu = new JPopupMenu(); JMenu columnMenu = tableMenu.getColumnMenu(); if (columnMenu != null) { for (Component component : columnMenu.getMenuComponents()) { //Clone! jTableHeaderPopupMenu.add(component); } } //SUM jTableHeaderPopupMenu.addSeparator(); AutoMenu<Q> jSum = tablePopupMenu.get(MenuEnum.SUM); if (jSum instanceof JMenuSum) { ((JMenuSum)jSum).updateMenuDataColumn(jTable.columnAtPoint(e.getPoint())); jTableHeaderPopupMenu.add(jSum.getComponent()); } jTableHeaderPopupMenu.show(e.getComponent(), e.getX(), e.getY()); } private void showTablePopupMenu(final MouseEvent e) { JPopupMenu jTablePopupMenu = new JPopupMenu(); selectClickedCell(e); //updateTableMenu(jTablePopupMenu); createMenu(jTablePopupMenu); jTablePopupMenu.show(e.getComponent(), e.getX(), e.getY()); } private void selectClickedCell(final MouseEvent e) { Object source = e.getSource(); if (source instanceof JTable) { JTable jSelectTable = (JTable) source; Point point = e.getPoint(); if (!jSelectTable.getVisibleRect().contains(point)) { //Ignore clickes outside table return; } int clickedRow = jSelectTable.rowAtPoint(point); int clickedColumn = jSelectTable.columnAtPoint(point); //Rows boolean clickInRowsSelection; if (jSelectTable.getRowSelectionAllowed()) { //clicked in selected rows? clickInRowsSelection = false; int[] selectedRows = jSelectTable.getSelectedRows(); for (int i = 0; i < selectedRows.length; i++) { if (selectedRows[i] == clickedRow) { clickInRowsSelection = true; break; } } } else { //Row selection not allowed - all rows selected clickInRowsSelection = true; } //Column boolean clickInColumnsSelection; if (jSelectTable.getColumnSelectionAllowed()) { //clicked in selected columns? clickInColumnsSelection = false; int[] selectedColumns = jSelectTable.getSelectedColumns(); for (int i = 0; i < selectedColumns.length; i++) { if (selectedColumns[i] == clickedColumn) { clickInColumnsSelection = true; break; } } } else { //Column selection not allowed - all columns selected clickInColumnsSelection = true; } //Clicked outside selection, select clicked cell if ( (!clickInRowsSelection || !clickInColumnsSelection) && clickedRow >= 0 && clickedColumn >= 0) { jSelectTable.setRowSelectionInterval(clickedRow, clickedRow); jSelectTable.setColumnSelectionInterval(clickedColumn, clickedColumn); } } } private class ListenerClass implements MouseListener, ListSelectionListener { int[] selectedColumns; int[] selectedRows; @Override public void mouseClicked(final MouseEvent e) { } @Override public void mousePressed(final MouseEvent e) { if (e.isPopupTrigger()) { if (e.getSource().equals(jTable)) { showTablePopupMenu(e); } if (jTable != null && e.getSource().equals(jTable.getTableHeader())) { showTableHeaderPopupMenu(e); } } } @Override public void mouseReleased(final MouseEvent e) { if (e.isPopupTrigger()) { if (e.getSource().equals(jTable)) { showTablePopupMenu(e); } if (jTable != null && e.getSource().equals(jTable.getTableHeader())) { showTableHeaderPopupMenu(e); } } } @Override public void mouseEntered(final MouseEvent e) { } @Override public void mouseExited(final MouseEvent e) { } @Override public void valueChanged(final ListSelectionEvent e) { if (!e.getValueIsAdjusting()) { if (selectedColumns == null || selectedRows == null || !Arrays.equals(selectedColumns, jTable.getSelectedColumns()) || !Arrays.equals(selectedRows, jTable.getSelectedRows())) { selectedColumns = jTable.getSelectedColumns(); selectedRows = jTable.getSelectedRows(); updateMainTableMenu(); } } } } public static void addSeparator(final JComponent jComponent) { if (jComponent instanceof JMenu) { JMenu jMenu = (JMenu) jComponent; jMenu.addSeparator(); } if (jComponent instanceof JPopupMenu) { JPopupMenu jPopupMenu = (JPopupMenu) jComponent; jPopupMenu.addSeparator(); } if (jComponent instanceof JDropDownButton) { JDropDownButton jDropDownButton = (JDropDownButton) jComponent; jDropDownButton.addSeparator(); } } protected static interface AutoMenu<T> { public void setMenuData(MenuData<T> menuData); public JComponent getComponent(); } protected abstract static class JAutoMenu<T> extends JMenu implements AutoMenu<T> { protected Program program; protected MenuData<T> menuData; public JAutoMenu(final String s, final Program program) { super(s); this.program = program; } @Override public final void setMenuData(MenuData<T> menuData) { this.menuData = menuData; updateMenuData(); } @Override public JComponent getComponent() { return this; } protected abstract void updateMenuData(); } protected abstract static class JAutoMenuComponent<T> implements AutoMenu<T> { protected Program program; protected MenuData<T> menuData; public JAutoMenuComponent(final Program program) { this.program = program; } @Override public final void setMenuData(MenuData<T> menuData) { this.menuData = menuData; updateMenuData(); } @Override public abstract JComponent getComponent(); protected abstract void updateMenuData(); } public static interface TableMenu<T> { public MenuData<T> getMenuData(); public JMenu getFilterMenu(); public JMenu getColumnMenu(); public void addInfoMenu(JComponent jComponent); public void addToolMenu(JComponent jComponent); } }