Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue #1045: Equipment filters #1055

Merged
merged 3 commits into from
Mar 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 57 additions & 58 deletions megameklab/src/megameklab/ui/util/AbstractEquipmentDatabaseView.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* MegaMekLab
* Copyright (c) 2021 - The MegaMek Team. All Rights Reserved.
* Copyright (c) 2021-2022 - The MegaMek Team. All Rights Reserved.
*
* This program 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
Expand Down Expand Up @@ -29,12 +29,13 @@

import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.event.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.TableModelEvent;
import javax.swing.table.TableColumn;
import javax.swing.table.TableRowSorter;
import java.awt.*;
import java.awt.event.*;
import java.util.List;
import java.util.*;
import java.util.stream.Collectors;

Expand Down Expand Up @@ -66,6 +67,7 @@ public abstract class AbstractEquipmentDatabaseView extends IView {
protected final JToggleButton showMissileButton = new JToggleButton(MISSILE.getDisplayName(), true);
protected final JToggleButton showArtilleryButton = new JToggleButton(ARTILLERY.getDisplayName());
protected final JToggleButton showPhysicalButton = new JToggleButton(PHYSICAL.getDisplayName());
protected final JToggleButton showIndustrialButton = new JToggleButton(INDUSTRIAL.getDisplayName());
protected final JToggleButton showCapitalButton = new JToggleButton(CAPITAL.getDisplayName());
protected final JToggleButton showAmmoButton = new JToggleButton(AMMO.getDisplayName());
protected final JToggleButton showOtherButton = new JToggleButton(OTHER.getDisplayName());
Expand All @@ -80,8 +82,14 @@ public abstract class AbstractEquipmentDatabaseView extends IView {
private final JButton tableModeButton = new JButton("Switch Table Columns");
private boolean tableMode = true;

private final List<JToggleButton> filterToggles = List.of(showEnergyButton, showBallisticButton, showMissileButton,
showArtilleryButton, showPhysicalButton, showCapitalButton, showAmmoButton, showOtherButton);
private final Map<JToggleButton, EquipmentDatabaseCategory> filterToggles = Map.of(showEnergyButton, ENERGY,
showBallisticButton, BALLISTIC, showMissileButton, MISSILE, showArtilleryButton, ARTILLERY,
showPhysicalButton, PHYSICAL, showCapitalButton, CAPITAL, showAmmoButton, AMMO, showOtherButton, OTHER,
showIndustrialButton, INDUSTRIAL);

private final Map<JToggleButton, EquipmentDatabaseCategory> hideToggles = Map.of(hideProtoButton, PROTOTYPE,
hideOneShotButton, ONE_SHOT, hideTorpedoButton, TORPEDO, hideAPButton, AP,
hideUnavailButton, UNAVAILABLE);

private static final String ADD_TEXT = " << Add ";

Expand Down Expand Up @@ -109,6 +117,7 @@ protected AbstractEquipmentDatabaseView(EntitySource eSource) {
setLayout(new BorderLayout());
add(setupControlPanel(), BorderLayout.PAGE_START);
add(new JScrollPane(masterEquipmentTable), BorderLayout.CENTER);
updateFilterToggleVisibility();
addListeners();
}

Expand Down Expand Up @@ -280,34 +289,19 @@ public void componentResized(ComponentEvent e) {
});

var showAllButton = new JButton("Show All");
showAllButton.addActionListener(this::showAllEquipmentTypes);
filterToggles.forEach(b -> b.addActionListener(this::filterToggleHandler));

if (getUsedButtons().contains(ENERGY)) {
buttonPanel.add(showEnergyButton);
}
if (getUsedButtons().contains(BALLISTIC)) {
buttonPanel.add(showBallisticButton);
}
if (getUsedButtons().contains(MISSILE)) {
buttonPanel.add(showMissileButton);
}
if (getUsedButtons().contains(ARTILLERY)) {
buttonPanel.add(showArtilleryButton);
}
if (getUsedButtons().contains(PHYSICAL)) {
buttonPanel.add(showPhysicalButton);
}
if (getUsedButtons().contains(CAPITAL)) {
buttonPanel.add(showCapitalButton);
}
if (getUsedButtons().contains(AMMO)) {
buttonPanel.add(showAmmoButton);
}
if (getUsedButtons().contains(OTHER)) {
buttonPanel.add(showOtherButton);
}
showAllButton.addActionListener(e -> showAllEquipmentTypes());
filterToggles.keySet().forEach(button -> button.addActionListener(this::filterToggleHandler));
buttonPanel.add(showEnergyButton);
buttonPanel.add(showBallisticButton);
buttonPanel.add(showMissileButton);
buttonPanel.add(showArtilleryButton);
buttonPanel.add(showPhysicalButton);
buttonPanel.add(showIndustrialButton);
buttonPanel.add(showCapitalButton);
buttonPanel.add(showAmmoButton);
buttonPanel.add(showOtherButton);
buttonPanel.add(showAllButton);
updateFilterToggleVisibility();

var buttonAndInfoPanel = Box.createVerticalBox();
if (CConfig.getBooleanParam(CConfig.NAG_EQUIPMENT_CTRLCLICK)) {
Expand All @@ -324,6 +318,20 @@ public void componentResized(ComponentEvent e) {
return typeFilterPanel;
}

private void updateFilterToggleVisibility() {
// Show/hide toggles as appropriate for this unit
filterToggles.forEach((toggle, category) -> toggle.setVisible(getUsedButtons().contains(category)));
hideToggles.forEach((toggle, category) -> toggle.setVisible(getUsedButtons().contains(category)));
// Deselect hidden toggles
filterToggles.entrySet().stream()
.filter(entry -> !getUsedButtons().contains(entry.getValue()))
.forEach(entry -> entry.getKey().setSelected(false));
hideToggles.entrySet().stream()
.filter(entry -> !getUsedButtons().contains(entry.getValue()))
.forEach(entry -> entry.getKey().setSelected(false));
}


/**
* Constructs and returns the Panel containing "Hide:" filter toggles
*/
Expand All @@ -339,26 +347,12 @@ public void componentResized(ComponentEvent e) {
}
});

if (getUsedButtons().contains(PROTOTYPE)) {
buttonPanel.add(hideProtoButton);
hideProtoButton.addItemListener(e -> equipmentSorter.sort());
}
if (getUsedButtons().contains(ONE_SHOT)) {
buttonPanel.add(hideOneShotButton);
hideOneShotButton.addItemListener(e -> equipmentSorter.sort());
}
if (getUsedButtons().contains(TORPEDO)) {
buttonPanel.add(hideTorpedoButton);
hideTorpedoButton.addItemListener(e -> equipmentSorter.sort());
}
if (getUsedButtons().contains(AP)) {
buttonPanel.add(hideAPButton);
hideAPButton.addItemListener(e -> equipmentSorter.sort());
}
if (getUsedButtons().contains(UNAVAILABLE)) {
buttonPanel.add(hideUnavailButton);
hideUnavailButton.addItemListener(e -> equipmentSorter.sort());
}
hideToggles.keySet().forEach(button -> button.addItemListener(e -> equipmentSorter.sort()));
buttonPanel.add(hideProtoButton);
buttonPanel.add(hideOneShotButton);
buttonPanel.add(hideTorpedoButton);
buttonPanel.add(hideAPButton);
buttonPanel.add(hideUnavailButton);

var hideFilterPanel = Box.createHorizontalBox();
hideFilterPanel.add(new JLabel("Hide: "));
Expand Down Expand Up @@ -412,7 +406,7 @@ public void removeUpdate(DocumentEvent e) {
}
if (useSwitchTableColumns()) {
miscPanel.add(tableModeButton);
tableModeButton.addActionListener(this::switchTableMode);
tableModeButton.addActionListener(e -> switchTableMode());
}
miscPanel.setBackground(UIUtil.alternateTableBGColor());
miscPanel.setOpaque(true);
Expand Down Expand Up @@ -441,6 +435,7 @@ public void setRefresh(RefreshListener newRefresh) {
}

public void refreshTable() {
updateFilterToggleVisibility();
updateVisibleColumns();
equipmentSorter.sort();
}
Expand All @@ -456,7 +451,7 @@ private void fireTableRefresh() {
}

/** Called from the Table Column Mode button to switch between two table column modes. */
private void switchTableMode(ActionEvent e) {
private void switchTableMode() {
tableMode = !tableMode;
updateVisibleColumns();
}
Expand All @@ -468,16 +463,19 @@ private void switchTableMode(ActionEvent e) {
*/
private void filterToggleHandler(ActionEvent e) {
if ((e.getModifiers() & ActionEvent.CTRL_MASK) == 0) {
filterToggles.forEach(button -> button.setSelected(e.getSource() == button));
filterToggles.keySet().forEach(button -> button.setSelected(e.getSource() == button));
}
equipmentSorter.sort();
}

/**
* Called from the Show All button to activate all type filter toggles.
* Called from the Show All button to activate all shown type filter toggles.
*/
private void showAllEquipmentTypes(ActionEvent e) {
filterToggles.forEach(t -> t.setSelected(true));
private void showAllEquipmentTypes() {
// Select all buttons that are displayed for this unit
filterToggles.entrySet().stream()
.filter(entry -> getUsedButtons().contains(entry.getValue()))
.forEach(entry -> entry.getKey().setSelected(true));
equipmentSorter.sort();
}

Expand Down Expand Up @@ -571,6 +569,7 @@ private boolean includedByFilters(EquipmentType equipment) {
|| (showBallisticButton.isSelected() && BALLISTIC.passesFilter(equipment, getEntity()))
|| (showArtilleryButton.isSelected() && ARTILLERY.passesFilter(equipment, getEntity()))
|| (showPhysicalButton.isSelected() && PHYSICAL.passesFilter(equipment, getEntity()))
|| (showIndustrialButton.isSelected() && INDUSTRIAL.passesFilter(equipment, getEntity()))
|| (showCapitalButton.isSelected() && CAPITAL.passesFilter(equipment, getEntity()))
|| (showAmmoButton.isSelected() && AMMO.passesFilter(equipment, getEntity()))
|| (showOtherButton.isSelected() && OTHER.passesFilter(equipment, getEntity()));
Expand Down
70 changes: 47 additions & 23 deletions megameklab/src/megameklab/ui/util/EquipmentDatabaseCategory.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Function;

import static megamek.common.EquipmentTypeLookup.*;
import static megamek.common.WeaponType.*;
import static megamek.common.MiscType.*;

Expand All @@ -36,8 +38,8 @@ public enum EquipmentDatabaseCategory {

ENERGY ("Energy",
(eq, en) -> (eq instanceof WeaponType) && !((WeaponType) eq).isCapital()
&& (eq.hasFlag(F_ENERGY)
|| ((eq.hasFlag(F_PLASMA) && (((WeaponType) eq).getAmmoType() == AmmoType.T_PLASMA))))),
&& (eq.hasFlag(F_ENERGY)
|| ((eq.hasFlag(F_PLASMA) && (((WeaponType) eq).getAmmoType() == AmmoType.T_PLASMA))))),

BALLISTIC ("Ballistic",
(eq, en) -> (eq instanceof WeaponType) && !((WeaponType) eq).isCapital() && eq.hasFlag(F_BALLISTIC)),
Expand All @@ -55,34 +57,40 @@ public enum EquipmentDatabaseCategory {
Entity::isLargeCraft),

PHYSICAL ("Physical",
(eq, en) -> UnitUtil.isPhysicalWeapon(eq),
(eq, en) -> UnitUtil.isPhysicalWeapon(eq) || isIndustrialEquipment(eq),
e -> e.hasETypeFlag(Entity.ETYPE_MECH)),

INDUSTRIAL ("Industrial",
(eq, en) -> isIndustrialEquipment(eq),
e -> (e instanceof Tank) || e.isSupportVehicle()),

AMMO ("Ammo",
(eq, en) -> (eq instanceof AmmoType) && !(eq instanceof BombType)
&& UnitUtil.canUseAmmo(en, (AmmoType) eq, false),
&& UnitUtil.canUseAmmo(en, (AmmoType) eq, false),
e -> e.getWeightClass() != EntityWeightClass.WEIGHT_SMALL_SUPPORT),

OTHER ("Other",
(eq, en) -> ((eq instanceof MiscType)
&& !UnitUtil.isPhysicalWeapon(eq)
&& !UnitUtil.isJumpJet(eq)
&& !UnitUtil.isHeatSink(eq)
&& !eq.hasFlag(F_TSM)
&& !eq.hasFlag(F_INDUSTRIAL_TSM)
&& !(eq.hasFlag(F_MASC)
&& !eq.hasSubType(S_SUPERCHARGER)
&& !eq.hasSubType(S_JETBOOSTER))
&& !(en.hasETypeFlag(Entity.ETYPE_QUADVEE) && eq.hasFlag(F_TRACKS))
&& !UnitUtil.isArmorOrStructure(eq)
&& !eq.hasFlag(F_CHASSIS_MODIFICATION)
&& !(en.isSupportVehicle() && (eq.hasFlag(F_BASIC_FIRECONTROL) || (eq.hasFlag(F_ADVANCED_FIRECONTROL))))
&& !eq.hasFlag(F_MAGNETIC_CLAMP)
&& !(eq.hasFlag(F_PARTIAL_WING) && en.hasETypeFlag(Entity.ETYPE_PROTOMECH))
&& !eq.hasFlag(F_SPONSON_TURRET)
&& !eq.hasFlag(F_PINTLE_TURRET))
|| (eq instanceof TAGWeapon)
|| ((eq instanceof AmmoType) && (((AmmoType) eq).getAmmoType() == AmmoType.T_COOLANT_POD))),
&& !UnitUtil.isPhysicalWeapon(eq)
&& !UnitUtil.isJumpJet(eq)
&& !UnitUtil.isHeatSink(eq)
&& !(isIndustrialEquipment(eq) && ((en instanceof Tank) || en.isSupportVehicle() || en instanceof Mech))
&& !eq.hasFlag(F_TSM)
&& !eq.hasFlag(F_INDUSTRIAL_TSM)
&& !(eq.hasFlag(F_MASC)
&& !eq.hasSubType(S_SUPERCHARGER)
&& !eq.hasSubType(S_JETBOOSTER))
&& !(en.hasETypeFlag(Entity.ETYPE_QUADVEE) && eq.hasFlag(F_TRACKS))
&& !UnitUtil.isArmorOrStructure(eq)
&& !(eq.hasFlag(F_CHASSIS_MODIFICATION) && en.isSupportVehicle())
&& !(en.isSupportVehicle() && (eq.hasFlag(F_BASIC_FIRECONTROL) || (eq.hasFlag(F_ADVANCED_FIRECONTROL))))
&& !eq.hasFlag(F_MAGNETIC_CLAMP)
&& !(eq.hasFlag(F_PARTIAL_WING) && en.hasETypeFlag(Entity.ETYPE_PROTOMECH))
&& !(eq.hasFlag(F_SPONSON_TURRET) && en.isSupportVehicle())
&& !eq.hasFlag(F_PINTLE_TURRET))
|| (eq instanceof TAGWeapon)
|| ((eq instanceof AmmoType) && (((AmmoType) eq).getAmmoType() == AmmoType.T_COOLANT_POD))
|| eq.hasFlag(F_AMS)),

AP ("Anti-Personnel",
(eq, en) -> UnitUtil.isBattleArmorAPWeapon(eq),
Expand All @@ -102,7 +110,7 @@ public enum EquipmentDatabaseCategory {
e -> !(e instanceof BattleArmor) && !(e instanceof Aero)),

UNAVAILABLE ("Unavailable")
// TODO: Provide MM.ITechManager.isLegal in static form
// TODO: Provide MM.ITechManager.isLegal in static form
;

private final static Set<EquipmentDatabaseCategory> showFilters = EnumSet.of(ENERGY, BALLISTIC, MISSILE,
Expand Down Expand Up @@ -156,4 +164,20 @@ public static Set<EquipmentDatabaseCategory> getShowFilters() {
public static Set<EquipmentDatabaseCategory> getHideFilters() {
return Collections.unmodifiableSet(hideFilters);
}

/**
* Returns true if the given equipment is an Industrial Equipment such as a Backhoe (TM, pp. 241-249).
* Note: This check has nothing to do with Industrial Meks.
*
* @param equipment The equipment to check
* @return true if the equipment is "Industrial" equipment
*/
public static boolean isIndustrialEquipment(EquipmentType equipment) {
return equipment.isAnyOf(BACKHOE, LIGHT_BRIDGE_LAYER, MEDIUM_BRIDGE_LAYER, HEAVY_BRIDGE_LAYER,
BULLDOZER, CHAINSAW, COMBINE, DUAL_SAW, DUMPER_FRONT, DUMPER_REAR, DUMPER_RIGHT, DUMPER_LEFT,
EXTENDED_FUEL_TANK, PILE_DRIVER, LADDER, LIFT_HOIST, MANIPULATOR_INDUSTRIAL, MINING_DRILL,
NAIL_RIVET_GUN, REFUELING_DROGUE, FLUID_SUCTION_LIGHT_MEK, FLUID_SUCTION_LIGHT_VEE,
FLUID_SUCTION, ROCK_CUTTER, SALVAGE_ARM, SPOT_WELDER, SPRAYER_MEK, SPRAYER_VEE, WRECKING_BALL);
}

}