@Override
  protected DataModel<LineChartDataSet> createChartModel(final User user, final String modelTitle) {
    final DataModel<LineChartDataSet> model =
        new DataModel<>(modelTitle, user.getPlayerColor().brighterColor, user);

    final Stat stat = statComboBox.getSelectedItem();

    int dsCount = 0;
    boolean gasOnly = false;
    if (stat.hasMinGas) {
      if (separateMinsGasCheckBox.isSelected()) {
        if (minsCheckBox.isSelected()) dsCount++;
        else gasOnly = true;
        if (gasCheckBox.isSelected()) dsCount++;
      } else dsCount = minsCheckBox.isSelected() || gasCheckBox.isSelected() ? 1 : 0;
    } else dsCount = stat.eventFields.length;

    for (int i = 0; i < dsCount; i++) {
      // Stroke will be set during reconfiguration
      final LineChartDataSet ds =
          new LineChartDataSet(
              i > 0 || gasOnly ? user.getPlayerColor().brighterColor : user.getPlayerColor().color,
              null,
              new int[0],
              new int[0]);
      model.addDataSet(ds);
    }

    return model;
  }
  @Override
  public List<Chart<LineChartDataSet>> createCharts() {
    chartList = new ArrayList<>();

    final TrackerEvents trackerEvents = repProc.replay.trackerEvents;
    if (trackerEvents == null) {
      chartsComp
          .getChartsCanvas()
          .setMessage(
              "This chart is available only from replay version 2.0.8. This replay has version "
                  + repProc.replay.header.versionString(false)
                  + ".");
      return chartList;
    }

    createModelByPlayerIds();

    switch (statComboBox.getSelectedItem()) {
      case SPENDING_QUOTIENT:
        // Spending Quotient is different than the others: it is not stored directly in the tracker
        // events stream
        calculateSQDataSets();
        break;
      case FOOD_MADE_USED:
        // Food Made+Used is showing 2 player stats at once: Food Made and Food Used
        calculateFoodMadeUsedDataSets();
        break;
      default:
        calculateDataSets();
        break;
    }

    return chartList;
  }
  /**
   * Creates a new {@link PlayerStatsChartFactory}.
   *
   * @param chartsComp reference to the charts component that created us
   */
  public PlayerStatsChartFactory(final ChartsComp chartsComp) {
    super(chartsComp);

    statComboBox.addActionListener(chartsComp.chartsRebuilder);
    statComboBox.addActionListener(
        new ActionAdapter(true) {
          @Override
          public void actionPerformed(final ActionEvent event) {
            final boolean hasMinGas = statComboBox.getSelectedItem().hasMinGas;
            minsCheckBox.setVisible(hasMinGas);
            gasCheckBox.setVisible(hasMinGas);
            separateMinsGasCheckBox.setVisible(hasMinGas);
          }
        });
    statComboBox.markSeparatedItems(
        Stat.FOOD_MADE_USED, Stat.ARMY_RES_IN_PROGRESS, Stat.ECON_RES_IN_PROGRESS);
    statComboBox.setMaximumRowCount(statComboBox.getItemCount());

    presentationComboBox.addActionListener(chartsComp.chartsReconfigurer);

    minsCheckBox.setText("Minerals");
    minsCheckBox.setToolTipText(Settings.PLAYER_STATS_INCLUDE_MINS.name);
    minsCheckBox.addActionListener(chartsComp.chartsRebuilder);

    gasCheckBox.setText("Gas");
    gasCheckBox.setToolTipText(Settings.PLAYER_STATS_INCLUDE_GAS.name);
    gasCheckBox.addActionListener(chartsComp.chartsRebuilder);

    separateMinsGasCheckBox.setText("Separate Mins/Gas");
    separateMinsGasCheckBox.setToolTipText(Settings.PLAYER_STATS_SEPARATE_MINS_GAS.name);
    separateMinsGasCheckBox.addActionListener(chartsComp.chartsRebuilder);
  }
  @Override
  public void reconfigureChartCanvas() {
    final boolean gasOnly =
        statComboBox.getSelectedItem().hasMinGas
            && separateMinsGasCheckBox.isSelected()
            && !minsCheckBox.isSelected();

    final Presentation p = presentationComboBox.getSelectedItem();

    for (final Chart<?> chart : chartsComp.getChartsCanvas().getChartList()) {
      if (chart instanceof LineTimeChart) {
        final LineTimeChart lc = (LineTimeChart) chart;
        lc.setPresentation(p);

        // Stroke depends on the presentation:
        for (final DataModel<LineChartDataSet> model : lc.getDataModelList()) {
          int i = 0;
          for (final LineChartDataSet dataSet : model.getDataSetList())
            dataSet.setStroke(i++ > 0 || gasOnly ? p.storke : p.storkeDouble);
        }
      }
    }
  }
 /** Calculates the data sets. */
 private void calculateDataSets() {
   calculateDataSets(statComboBox.getSelectedItem());
 }
Exemple #6
0
  @Override
  protected void buildGui() {
    XToolBar toolBar = new XToolBar();
    toolBarsBox.add(toolBar);

    toolBar.add(new XLabel("Data source:").verticalBorder(7));
    dataSourceComboBox.addActionListener(rebuilderListener);
    dataSourceComboBox.setMaximumRowCount(dataSourceComboBox.getItemCount());
    toolBar.add(dataSourceComboBox);

    toolBar.addSeparator();
    LGuiUtils.autoCreateDisabledImage(toolBar.add(saveAction));

    toolBar.addSeparator();
    toolBar.add(new XLabel("Line size:"));
    toolBar.add(hexLineSizeComboBox);
    toolBar.add(new XLabel(Settings.HEX_LINE_SIZE.viewHints.getSubsequentText()));

    toolBar.addSeparator();
    toolBar.add(new XLabel("Jump to pos:"));
    final XTextField jumpTextField = new XTextField(null, 6, true);
    jumpTextField.setValidator(
        new IValidator() {
          @Override
          public boolean validate(final String text) {
            final boolean result = validateLogic(text);
            if (!result) Sound.beepOnEmptyTxtSearchRslt();
            return result;
          }

          private boolean validateLogic(final String text) {
            if (text.isEmpty() || "0x".equals(text)) return true;
            try {
              final int pos =
                  text.startsWith("0x")
                      ? Integer.parseInt(text.substring(2), 16)
                      : Integer.parseInt(text);
              if (pos < 0 || pos >= data.length) return false;
              final int line = pos >> hexLineSizeComboBox.getSelectedItem().shiftCount;
              textList.setSelectedIndex(line);
              textList.scrollRectToVisible(textList.getCellBounds(line, line));
              return true;
            } catch (final NumberFormatException nfe) {
              return false;
            }
          }
        });
    jumpTextField.addActionListener(
        new ActionAdapter() {
          @Override
          public void actionPerformed(final ActionEvent event) {
            final String text = jumpTextField.getText();
            jumpTextField.setText(null);
            jumpTextField.setText(text);
          }
        });
    jumpTextField.setToolTipText(
        "<html>Enter a byte position to jump to, either decimal or hexa starting with <code>\"0x\"</code>.</html>");
    // Focus Jump field when pressing CTRL+J
    jumpTextField.registerFocusHotkey(
        this, KeyStroke.getKeyStroke(KeyEvent.VK_J, InputEvent.CTRL_MASK));
    toolBar.add(jumpTextField);

    toolBar.addSeparator();
    toolBar.add(dataSizeLabel);

    toolBar.finalizeLayout();

    super.buildGui();

    // Extend the original tool bar
    toolBar = this.toolBar;
    // Move the info component (currently the last) to the end
    final Component infoComp = toolBar.getComponent(toolBar.getComponentCount() - 1);
    toolBar.remove(toolBar.getComponentCount() - 1);
    // Insert tip to the text search, before the separator:
    toolBar.add(new TipIcon(Tips.BINARY_DATA_TEXT_SEARCH), toolBar.getComponentCount() - 1);
    // Add Hex searcher
    hexSearchComp.textField.setValidator(
        new IValidator() {
          @Override
          public boolean validate(String text) {
            text = text.replace(" ", ""); // Remove spaces
            // Check: length must be even, must contain only hex digits
            return (text.length() & 0x01) == 0 && text.matches("[\\da-f]*");
          }
        });
    hexSearchComp.textField.setToolTipText(
        "<html>Search hex data, for example <code>\"06 1f\"</html>");
    // Register CTRL+F for hex search (CTRL+S is taken by the other search component)
    // (CTRL+H is already used by JTextField!)
    hexSearchComp.registerFocusHotkey(
        this, KeyStroke.getKeyStroke(KeyEvent.VK_F, InputEvent.CTRL_MASK));
    hexSearchComp.setSearcher(hexSearcher);
    toolBar.add(hexSearchComp);
    toolBar.addSeparator();
    toolBar.add(infoComp);
    toolBar.finalizeLayout();
  }
Exemple #7
0
  /**
   * Creates a new {@link BinaryDataComp}.
   *
   * @param repProc replay processor
   */
  public BinaryDataComp(final RepProcessor repProc) {
    super(repProc);

    hexLineSizeComboBox =
        SettingsGui.createSettingComboBox(
            Settings.HEX_LINE_SIZE, Env.APP_SETTINGS, rebuilderListener);

    final Vector<DataSource> sourceVector = new Vector<>();
    final List<DataSource> separatedDsList = new ArrayList<>();

    // The replay file
    sourceVector.add(new FullFileDataSource("Replay File", repProc.file));
    separatedDsList.add(sourceVector.get(sourceVector.size() - 1));
    // General Replay MPQ content
    sourceVector.add(new MpqUserDataDataSource(repProc.file));
    for (final MpqContent mpqContent : MpqContent.VALUES)
      sourceVector.add(new MpqContentDataSource(repProc.file, mpqContent));
    separatedDsList.add(sourceVector.get(sourceVector.size() - 1));
    // Replay content
    for (final RepContent mpqContent : RepContent.VALUES) {
      sourceVector.add(new MpqContentDataSource(repProc.file, mpqContent));
      if (mpqContent == RepContent.TRACKER_EVENTS)
        separatedDsList.add(sourceVector.get(sourceVector.size() - 1));
    }
    separatedDsList.add(sourceVector.get(sourceVector.size() - 1));
    // General Map MPQ content
    final Path mapFile = MapParser.getMapFile(repProc);
    sourceVector.add(new MpqUserDataDataSource(mapFile));
    for (final MpqContent mpqContent : MpqContent.VALUES)
      sourceVector.add(new MpqContentDataSource(mapFile, mpqContent));
    separatedDsList.add(sourceVector.get(sourceVector.size() - 1));
    // Map content
    for (final MapContent mpqContent : MapContent.VALUES)
      sourceVector.add(new MpqContentDataSource(mapFile, mpqContent));

    dataSourceComboBox = new XComboBox<>(sourceVector);
    dataSourceComboBox.markSeparatedItems(separatedDsList);

    searcher =
        new ByteIntPosBaseSearcher() {
          /** Bytes of the search text, as <code>int</code>s. */
          private int[] searchTextBytes;

          @Override
          protected void prepareNew() {
            // Note: Texts found in binary data are usually UTF-8 encoded.
            // This text search converts the searched text to bytes using the same
            // encoding (UTF-8), and this byte sequence will be searched.
            // For more details see the Tips.BINARY_DATA_TEXT_SEARCH tip.
            final byte[] bytes = searchText.getBytes(Env.UTF8);
            searchTextBytes = new int[bytes.length];
            maxOffset = searchTextBytes.length - 1;
            for (int i = maxOffset; i >= 0; i--) searchTextBytes[i] = bytes[i] & 0xff;
            super.prepareNew();
          }

          @Override
          public boolean matches() {
            // Local vars for fast access
            final byte[] data = BinaryDataComp.this.data;
            final int[] searchTextBytes = this.searchTextBytes;
            final int searchPos = this.searchPos;
            if (searchPos + maxOffset > maxPos)
              return false; // Would overflow in data, surely cannot match

            for (int offset = maxOffset; offset >= 0; offset--) {
              final int searchChar = searchTextBytes[offset];
              final int ch = data[searchPos + offset] & 0xff;
              if (ch != searchChar) {
                // Also check in a case insensitive manner (lower-cased version of ch):
                if (ch >= 'A' && ch <= 'Z') {
                  if (ch - ('A' - 'a') != searchChar) return false;
                } else return false;
              }
            }

            // Match!
            handleMatch();
            // Clear the other searcher (so it will start from this line)
            hexSearcher.clearLastSearchPos();

            return true;
          }
        };

    hexSearcher =
        new ByteIntPosBaseSearcher() {
          /** Bytes specified by the hex search text. */
          private byte[] searchTextBytes;

          @Override
          protected void prepareNew() {
            searchTextBytes = Utils.hexToBytes(searchText);
            if (searchTextBytes == null) searchTextBytes = new byte[0];
            maxOffset = searchTextBytes.length - 1;
            super.prepareNew();
          }

          @Override
          public boolean matches() {
            if (searchTextBytes.length == 0) return true; // To end the search
            // Local vars for fast access
            final byte[] data = BinaryDataComp.this.data;
            final byte[] searchTextBytes = this.searchTextBytes;
            final int searchPos = this.searchPos;
            if (searchPos + maxOffset > maxPos)
              return false; // Would overflow in data, surely cannot match

            for (int offset = maxOffset; offset >= 0; offset--)
              if (searchTextBytes[offset] != data[searchPos + offset]) return false;

            // Match!
            handleMatch();
            // Clear the other searcher (so it will start from this line)
            searcher.clearLastSearchPos();

            return true;
          }
        };

    buildGui();
  }