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

Added 'Part Quality Report' Dialog #4888

Merged
merged 3 commits into from
Sep 25, 2024
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
4 changes: 4 additions & 0 deletions MekHQ/resources/mekhq/resources/GUI.properties
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,10 @@ HangarReportDialog.title=Hangar Report
MaintenanceReportDialog.title=Maintenance Report
MaintenanceReportDialog.Unit.title=Maintenance Report for %s

### PartsReportDialog Class
PartQualityReportDialog.title=Part Quality Report
PartQualityReportDialog.Unit.title=Parts Quality Report for %s

### MonthlyUnitCostReportDialog Class
MonthlyUnitCostReportDialog.title=Monthly Cost Report
MonthlyUnitCostReportDialog.Unit.title=Monthly Cost Report for %s
Expand Down
4 changes: 2 additions & 2 deletions MekHQ/src/mekhq/campaign/personnel/death/AbstractDeath.java
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ public Map<Gender, Map<TenYearAgeRange, WeightedDoubleMap<PersonnelStatus>>> get

/**
* This is used to determine if a person can die.
*
*
* @param person the person to determine for
* @param ageGroup the age group of the person in question
* @param randomDeath if this is for random death or manual death
Expand Down Expand Up @@ -144,7 +144,7 @@ public Map<Gender, Map<TenYearAgeRange, WeightedDoubleMap<PersonnelStatus>>> get
// region New Day
/**
* Processes new day random death for an individual.
*
*
* @param campaign the campaign to process
* @param today the current day
* @param person the person to process
Expand Down
64 changes: 25 additions & 39 deletions MekHQ/src/mekhq/gui/adapter/UnitTableMouseAdapter.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,30 +18,6 @@
*/
package mekhq.gui.adapter;

import static megamek.client.ui.WrapLayout.wordWrap;

import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.ResourceBundle;
import java.util.UUID;
import java.util.Vector;
import java.util.stream.Stream;

import javax.swing.JCheckBoxMenuItem;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.JSplitPane;
import javax.swing.JTable;

import megamek.client.ui.dialogs.BVDisplayDialog;
import megamek.client.ui.dialogs.CamoChooserDialog;
import megamek.client.ui.swing.UnitEditorDialog;
Expand All @@ -65,33 +41,33 @@
import mekhq.campaign.parts.equipment.AmmoBin;
import mekhq.campaign.personnel.Person;
import mekhq.campaign.unit.Unit;
import mekhq.campaign.unit.actions.ActivateUnitAction;
import mekhq.campaign.unit.actions.CancelMothballUnitAction;
import mekhq.campaign.unit.actions.HirePersonnelUnitAction;
import mekhq.campaign.unit.actions.IUnitAction;
import mekhq.campaign.unit.actions.MothballUnitAction;
import mekhq.campaign.unit.actions.RestoreUnitAction;
import mekhq.campaign.unit.actions.StripUnitAction;
import mekhq.campaign.unit.actions.SwapAmmoTypeAction;
import mekhq.campaign.unit.actions.*;
import mekhq.gui.CampaignGUI;
import mekhq.gui.HangarTab;
import mekhq.gui.MekLabTab;
import mekhq.gui.dialog.BombsDialog;
import mekhq.gui.dialog.ChooseRefitDialog;
import mekhq.gui.dialog.LargeCraftAmmoSwapDialog;
import mekhq.gui.dialog.MarkdownEditorDialog;
import mekhq.gui.dialog.MassMothballDialog;
import mekhq.gui.dialog.QuirksDialog;
import mekhq.gui.dialog.SmallSVAmmoSwapDialog;
import mekhq.gui.dialog.*;
import mekhq.gui.dialog.reportDialogs.MaintenanceReportDialog;
import mekhq.gui.dialog.reportDialogs.MonthlyUnitCostReportDialog;
import mekhq.gui.dialog.reportDialogs.PartQualityReportDialog;
import mekhq.gui.enums.MHQTabType;
import mekhq.gui.menus.AssignUnitToPersonMenu;
import mekhq.gui.menus.ExportUnitSpriteMenu;
import mekhq.gui.model.UnitTableModel;
import mekhq.gui.utilities.JMenuHelpers;
import mekhq.gui.utilities.StaticChecks;

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.*;
import java.util.stream.Stream;

import static megamek.client.ui.WrapLayout.wordWrap;

public class UnitTableMouseAdapter extends JPopupMenuAdapter {
private static final MMLogger logger = MMLogger.create(UnitTableMouseAdapter.class);

Expand Down Expand Up @@ -129,6 +105,7 @@ public class UnitTableMouseAdapter extends JPopupMenuAdapter {
public static final String COMMAND_QUIRKS = "QUIRKS";
public static final String COMMAND_BOMBS = "BOMBS";
public static final String COMMAND_SUPPLY_COST = "SUPPLY_COST";
public static final String COMMAND_PARTS_REPORT = "PARTS_REPORT";
public static final String COMMAND_TAG_CUSTOM = "TAG_CUSTOM";
public static final String COMMAND_INDI_CAMO = "INDI_CAMO";
public static final String COMMAND_REMOVE_INDI_CAMO = "REMOVE_INDI_CAMO";
Expand Down Expand Up @@ -205,6 +182,8 @@ public void actionPerformed(ActionEvent action) {
new MaintenanceReportDialog(gui.getFrame(), selectedUnit).setVisible(true);
} else if (command.equals(COMMAND_SUPPLY_COST)) { // Single Unit only
new MonthlyUnitCostReportDialog(gui.getFrame(), selectedUnit).setVisible(true);
} else if (command.equals(COMMAND_PARTS_REPORT)) { // Single Unit only
new PartQualityReportDialog(gui.getFrame(), selectedUnit).setVisible(true);
} else if (command.equals(COMMAND_SET_QUALITY)) {
// TODO : Duplicated in PartsTableMouseAdapter#actionPerformed
int q = -1;
Expand Down Expand Up @@ -819,6 +798,13 @@ protected Optional<JPopupMenu> createPopupMenu() {
popup.add(menuItem);
}

if (oneSelected && gui.getCampaign().getCampaignOptions().isCheckMaintenance()) {
menuItem = new JMenuItem("Show Part Quality Report");
menuItem.setActionCommand(COMMAND_PARTS_REPORT);
menuItem.addActionListener(this);
popup.add(menuItem);
}

if (areAllConventionalInfantry) {
menuItem = new JMenuItem("Disband");
menuItem.setActionCommand(COMMAND_DISBAND);
Expand Down
167 changes: 167 additions & 0 deletions MekHQ/src/mekhq/gui/dialog/reportDialogs/PartQualityReportDialog.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
/*
* Copyright (c) 2024 - The MegaMek Team. All Rights Reserved.
*
* This file is part of MekHQ.
*
* MekHQ 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 3 of the License, or
* (at your option) any later version.
*
* MekHQ 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 MekHQ. If not, see <http://www.gnu.org/licenses/>.
*/
package mekhq.gui.dialog.reportDialogs;

import mekhq.MekHQ;
import mekhq.campaign.parts.Part;
import mekhq.campaign.unit.Unit;
import mekhq.utilities.ReportingUtilities;

import javax.swing.*;
import java.util.*;

import static mekhq.campaign.parts.Part.QUALITY_A;
import static mekhq.campaign.parts.Part.QUALITY_B;
import static mekhq.campaign.parts.Part.QUALITY_E;
import static mekhq.campaign.parts.Part.QUALITY_F;
import static mekhq.utilities.ReportingUtilities.CLOSING_SPAN_TAG;

/**
* Represents a dialog for generating a part quality report.
* Extends the {@link AbstractReportDialog} class.
*/
public class PartQualityReportDialog extends AbstractReportDialog {
//region Variable Declarations
private final Unit unit;
//endregion Variable Declarations

//region Constructors
/**
* Constructs a new instance of {@link PartQualityReportDialog}.
*
* @param frame the parent {@link JFrame}
* @param unit the unit for which the parts quality report is being generated
*/
public PartQualityReportDialog(final JFrame frame, final Unit unit) {
super(frame, "PartQualityReportDialog", "PartQualityReportDialog.title");
this.unit = unit;
setTitle(String.format(resources.getString("PartQualityReportDialog.Unit.title"),
unit.getName()));
initialize();
}
//endregion Constructors

//region Getters
/**
* @return the unit associated with this object
*/
public Unit getUnit() {
return unit;
}

@Override
protected JTextPane createTxtReport() {
final JTextPane txtReport = new JTextPane();
txtReport.setContentType("text/html");
txtReport.setText(getPartsReport(getUnit()));
txtReport.setName("txtReport");
txtReport.setEditable(false);
txtReport.setCaretPosition(0);
return txtReport;
}
//endregion Getters

/**
* Produces a Part Quality report for the given unit. The report includes each part's location,
* name, and quality.
*
* @param unit The unit to generate a report for.
* @return An HTML string displaying the status of each part in the unit.
*/
private String getPartsReport(Unit unit) {
// This map will hold part lists, keyed by the location on the unit.
Map<String, List<Part>> reportMap = new HashMap<>();

// Iterate over parts, assigning each to its location in the map.
for (Part part : unit.getParts()) {
String location = part.getLocationName() != null ? part.getLocationName() : unit.getName();
reportMap.computeIfAbsent(location, k -> new ArrayList<>()).add(part);
}

// Create a sorted list of locations, excluding the unit's name.
List<String> locations = new ArrayList<>();
for (String location : reportMap.keySet()) {
if (!location.equals(unit.getName())) {
locations.add(location);
}
}
Collections.sort(locations);

// Add the unit's name to the start of the sorted locations list.
locations.add(0, unit.getName());

// Begin the HTML report.
StringBuilder report = new StringBuilder("<html>");

// For each location, add reported details about that location's parts.
for (String location : locations) {
report.append("<b>");
if (location.equals(unit.getName())) {
String colorCode = getColorCode(unit.getQuality());

// Add the location and its colored quality rating to the report.
report.append("<span style=\"font-size: 18px;\">")
.append(location)
.append(" - ");
report.append("<span style=\"color: ")
.append(colorCode)
.append(";\">")
.append(unit.getQualityName())
.append("</span>");
report.append("</span>");
} else {
report.append("<span style=\"font-size: 12px;\">").append(location).append("</span>");
}
report.append("</b><br>");

// For each part in the current location, add it to the report.
for (Part part : reportMap.get(location)) {
report.append(part.getName()).append(" - ");

int qualityLevel = part.getQuality();
String colorCode = getColorCode(qualityLevel);

report.append(ReportingUtilities.spanOpeningWithCustomColor(colorCode))
.append(part.getQualityName()).append(CLOSING_SPAN_TAG).append("<br>");
}

// Add a line break between locations.
report.append("<br>");
}

// Finish the HTML report.
report.append("</html>");

return report.toString();
}

/**
* Returns the color code associated with the given quality level.
*
* @param qualityLevel The quality level for which to retrieve the color code.
* @return The color code associated with the quality level.
*/
private static String getColorCode(int qualityLevel) {
return switch (qualityLevel) {
case QUALITY_A, QUALITY_B -> MekHQ.getMHQOptions().getFontColorNegativeHexColor();
case QUALITY_E, QUALITY_F -> MekHQ.getMHQOptions().getFontColorPositiveHexColor();
default -> MekHQ.getMHQOptions().getFontColorWarningHexColor();
};
}
}