@Override
 public boolean include(
     RowFilter.Entry<? extends PersonnelTableModel, ? extends Integer> entry) {
   Person p = entry.getModel().getPerson(entry.getIdentifier().intValue());
   if (!p.isActive()) {
     return false;
   }
   if (!prisoners && (p.isPrisoner() || p.isBondsman())) {
     return false;
   }
   if ((null != primaryRole) && (p.getPrimaryRole() != primaryRole.intValue())) {
     return false;
   }
   if ((null != expLevel) && (p.getExperienceLevel(false) != expLevel.intValue())) {
     return false;
   }
   if ((null != skill)) {
     Skill s = p.getSkill(skill);
     if (null == s) {
       int cost = SkillType.getType(skill).getCost(0);
       return (cost >= 0) && (cost <= p.getXp());
     } else {
       int cost = s.getCostToImprove();
       return (s.getLevel() < maxSkillLevel) && (cost >= 0) && (cost <= p.getXp());
     }
   }
   return true;
 }
  protected void spendXP() {
    String skillName = (String) choiceSkill.getSelectedItem();
    if (choiceNoSkill.equals(skillName)) {
      // This shouldn't happen, but guard against it anyway.
      return;
    }
    int rows = personnelTable.getRowCount();
    int improvedPersonnelCount = rows;
    while (rows > 0) {
      for (int i = 0; i < rows; ++i) {
        Person p = personnelModel.getPerson(personnelTable.convertRowIndexToModel(i));
        int cost = 0;
        if (p.hasSkill(skillName)) {
          cost = p.getCostToImprove(skillName);
        } else {
          cost = SkillType.getType(skillName).getCost(0);
        }
        int experience = p.getExperienceLevel(false);

        // Improve the skill and deduce the cost
        p.improveSkill(skillName);
        campaign.personUpdated(p);
        p.setXp(p.getXp() - cost);

        // The next part is bollocks and doesn't belong here, but as long as we hardcode AtB ...
        if (campaign.getCampaignOptions().getUseAtB()) {
          if ((p.getPrimaryRole() > Person.T_NONE)
              && (p.getPrimaryRole() <= Person.T_CONV_PILOT)
              && (p.getExperienceLevel(false) > experience)
              && (experience >= SkillType.EXP_REGULAR)) {
            String spa = campaign.rollSPA(p.getPrimaryRole(), p);
            if (null == spa) {
              if (campaign.getCampaignOptions().useEdge()) {
                p.acquireAbility(
                    PilotOptions.EDGE_ADVANTAGES, "edge", p.getEdge() + 1); // $NON-NLS-1$
                p.addLogEntry(
                    campaign.getDate(),
                    String.format(resourceMap.getString("gainedEdge.text"))); // $NON-NLS-1$
              }
            } else {
              p.addLogEntry(
                  campaign.getDate(),
                  String.format(resourceMap.getString("gained.format"), spa)); // $NON-NLS-1$
            }
          }
        }
      }
      // Refresh the filter and continue if we still have anyone available
      updatePersonnelTable();
      rows = personnelTable.getRowCount();
      dataChanged = true;
    }
    if (improvedPersonnelCount > 0) {
      campaign.addReport(
          String.format(
              resourceMap.getString("improvedSkills.format"),
              skillName,
              improvedPersonnelCount)); //$NON-NLS-1$
    }
  }
  public String getDesc() {
    if ((!isBreached() && !isBlownOff()) || isSalvaging()) {
      return super.getDesc();
    }
    String toReturn = "<html><font size='2'";
    String scheduled = "";
    if (getAssignedTeamId() != null) {
      scheduled = " (scheduled) ";
    }

    toReturn += ">";
    if (isBlownOff()) {
      toReturn += "<b>Re-attach " + getName() + "</b><br/>";
    } else {
      toReturn += "<b>Seal " + getName() + "</b><br/>";
    }
    toReturn += getDetails() + "<br/>";
    if (getSkillMin() > SkillType.EXP_ELITE) {
      toReturn += "<font color='red'>Impossible</font>";
    } else {
      toReturn += "" + getTimeLeft() + " minutes" + scheduled;
      if (isBlownOff()) {
        String bonus = getAllMods(null).getValueAsString();
        if (getAllMods(null).getValue() > -1) {
          bonus = "+" + bonus;
        }
        bonus = "(" + bonus + ")";
        if (!getCampaign().getCampaignOptions().isDestroyByMargin()) {
          toReturn += ", " + SkillType.getExperienceLevelName(getSkillMin());
        }
        toReturn += " " + bonus;
        if (getMode() != Modes.MODE_NORMAL) {
          toReturn += "<br/><i>" + getCurrentModeName() + "</i>";
        }
      }
    }
    toReturn += "</font></html>";
    return toReturn;
  }
  private JComponent getButtonPanel() {
    JPanel panel = new JPanel();
    panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));

    choiceType = new JComboBox<PersonTypeItem>();
    choiceType.setMaximumSize(
        new Dimension(Short.MAX_VALUE, (int) choiceType.getPreferredSize().getHeight()));
    DefaultComboBoxModel<PersonTypeItem> personTypeModel = new DefaultComboBoxModel<>();
    personTypeModel.addElement(
        new PersonTypeItem(resourceMap.getString("primaryRole.choice.text"), null)); // $NON-NLS-1$
    for (int i = 1; i < Person.T_NUM; ++i) {
      personTypeModel.addElement(
          new PersonTypeItem(Person.getRoleDesc(i, campaign.getFaction().isClan()), i));
    }
    personTypeModel.addElement(
        new PersonTypeItem(Person.getRoleDesc(0, campaign.getFaction().isClan()), 0));
    // Add "none" for generic AsTechs
    choiceType.setModel(personTypeModel);
    choiceType.setSelectedIndex(0);
    choiceType.addActionListener(
        e -> {
          personnelFilter.setPrimaryRole(((PersonTypeItem) choiceType.getSelectedItem()).id);
          updatePersonnelTable();
        });
    panel.add(choiceType);

    choiceExp = new JComboBox<PersonTypeItem>();
    choiceExp.setMaximumSize(
        new Dimension(Short.MAX_VALUE, (int) choiceType.getPreferredSize().getHeight()));
    DefaultComboBoxModel<PersonTypeItem> personExpModel = new DefaultComboBoxModel<>();
    personExpModel.addElement(
        new PersonTypeItem(resourceMap.getString("experience.choice.text"), null)); // $NON-NLS-1$
    for (int i = 0; i < 5; ++i) {
      personExpModel.addElement(new PersonTypeItem(SkillType.getExperienceLevelName(i), i));
    }
    choiceExp.setModel(personExpModel);
    choiceExp.setSelectedIndex(0);
    choiceExp.addActionListener(
        e -> {
          personnelFilter.setExpLevel(((PersonTypeItem) choiceExp.getSelectedItem()).id);
          updatePersonnelTable();
        });
    panel.add(choiceExp);

    choiceSkill = new JComboBox<String>();
    choiceSkill.setMaximumSize(
        new Dimension(Short.MAX_VALUE, (int) choiceSkill.getPreferredSize().getHeight()));
    DefaultComboBoxModel<String> personSkillModel = new DefaultComboBoxModel<>();
    personSkillModel.addElement(choiceNoSkill);
    for (String skill : SkillType.getSkillList()) {
      personSkillModel.addElement(skill);
    }
    choiceSkill.setModel(personSkillModel);
    choiceSkill.setSelectedIndex(0);
    choiceSkill.addActionListener(
        e -> {
          if (choiceNoSkill.equals(choiceSkill.getSelectedItem())) {
            personnelFilter.setSkill(null);
            ((SpinnerNumberModel) skillLevel.getModel()).setMaximum(10);
            buttonSpendXP.setEnabled(false);
          } else {
            String skillName = (String) choiceSkill.getSelectedItem();
            personnelFilter.setSkill(skillName);
            int maxSkillLevel = SkillType.getType(skillName).getMaxLevel();
            int currentLevel = (Integer) skillLevel.getModel().getValue();
            ((SpinnerNumberModel) skillLevel.getModel()).setMaximum(maxSkillLevel);
            if (currentLevel > maxSkillLevel) {
              skillLevel.getModel().setValue(Integer.valueOf(maxSkillLevel));
            }
            buttonSpendXP.setEnabled(true);
          }
          updatePersonnelTable();
        });
    panel.add(choiceSkill);

    panel.add(Box.createRigidArea(new Dimension(10, 10)));
    panel.add(new JLabel(resourceMap.getString("targetSkillLevel.text"))); // $NON-NLS-1$

    skillLevel = new JSpinner(new SpinnerNumberModel(10, 0, 10, 1));
    skillLevel.setMaximumSize(
        new Dimension(Short.MAX_VALUE, (int) skillLevel.getPreferredSize().getHeight()));
    skillLevel.addChangeListener(
        e -> {
          personnelFilter.setMaxSkillLevel((Integer) skillLevel.getModel().getValue());
          updatePersonnelTable();
        });
    panel.add(skillLevel);

    allowPrisoners = new JCheckBox(resourceMap.getString("allowPrisoners.text")); // $NON-NLS-1$
    allowPrisoners.setHorizontalAlignment(SwingConstants.LEFT);
    allowPrisoners.setMaximumSize(
        new Dimension(Short.MAX_VALUE, (int) allowPrisoners.getPreferredSize().getHeight()));
    allowPrisoners.addChangeListener(
        e -> {
          personnelFilter.setAllowPrisoners(allowPrisoners.isSelected());
          updatePersonnelTable();
        });
    JPanel allowPrisonersPanel = new JPanel(new GridLayout(1, 1));
    allowPrisonersPanel.setAlignmentY(JComponent.LEFT_ALIGNMENT);
    allowPrisonersPanel.add(allowPrisoners);
    allowPrisonersPanel.setMaximumSize(
        new Dimension(Short.MAX_VALUE, (int) allowPrisonersPanel.getPreferredSize().getHeight()));
    panel.add(allowPrisonersPanel);

    panel.add(Box.createVerticalGlue());

    matchedPersonnelLabel = new JLabel(""); // $NON-NLS-1$
    matchedPersonnelLabel.setMaximumSize(
        new Dimension(Short.MAX_VALUE, (int) matchedPersonnelLabel.getPreferredSize().getHeight()));
    panel.add(matchedPersonnelLabel);

    JPanel buttons = new JPanel(new FlowLayout());
    buttons.setMaximumSize(
        new Dimension(Short.MAX_VALUE, (int) buttons.getPreferredSize().getHeight()));

    buttonSpendXP = new JButton(resourceMap.getString("spendXP.text")); // $NON-NLS-1$
    buttonSpendXP.setEnabled(false);
    buttonSpendXP.addActionListener(e -> spendXP());
    buttons.add(buttonSpendXP);

    JButton button = new JButton(resourceMap.getString("close.text")); // $NON-NLS-1$
    button.addActionListener(e -> setVisible(false));
    buttons.add(button);

    panel.add(buttons);

    panel.setMaximumSize(new Dimension((int) panel.getPreferredSize().getWidth(), Short.MAX_VALUE));
    panel.setMinimumSize(new Dimension((int) panel.getPreferredSize().getWidth(), 300));

    return panel;
  }
 public String getAverageExperience() {
   return SkillType.getExperienceLevelName(
       calcAverageExperience().setScale(0, RoundingMode.HALF_UP).intValue());
 }