/**
   * Called when one of the sliding window size or step values has changed. This attempts to be
   * 'smart' and instead of recalculating everything from scratch,
   */
  private void newWindowSizeAction() {
    if (sgSeriesMap == null) return;

    XYSeries data;
    int windowSize = (Integer) windowSizeSpinner.getValue();
    int windowStep = (Integer) windowStepSpinner.getValue();
    int tot = 0;

    for (AbstractSeries series : chart.getAllSeries()) {
      if (series instanceof BaseCounterSeries) {
        BaseCounterSeries bcSer = (BaseCounterSeries) series;
        BaseCounter bc = bcSer.getCalculator();
        int partitionIndex = bcSer.getPartitionIndex();
        // System.out.println("Replacing series for calc with name : " + bcSer.getName() + " and
        // partition : " + bcSer.getPartitionIndex());
        if (partitionIndex > -1) {
          bcSer.replaceSeries(bc.getWindowPointSeries(windowSize, windowStep, partitionIndex));
        } else bcSer.replaceSeries(bc.getWindowPointSeries(windowSize, windowStep));
      } else {
        // System.err.println("Hmm, one of the series (" + series.getName() + ") is not a
        // basecounter, cannot recalculate it for the new window size.");/
      }
    }

    chart.repaint();
  }
 /**
  * Adjusts the x min and max bounds of the chart to match the selection region. Has no effect if
  * there is no selected region
  */
 protected void zoomToSelection() {
   if (chart.isRangeSelected()) {
     double[] range = chart.getRangeSelection();
     if (range[0] < range[1]) {
       chart.setXMin(range[0]);
       chart.setXMax(range[1]);
       chart.clearRangeSelection();
       repaint();
     }
   }
 }
 /**
  * You guessed it..remove from the chart all series that are a) baseCounters, and b), have the
  * baseCounter name of calcName. This happens when the user toggles off the check box associated
  * with the named calculator.
  *
  * @param calcName
  */
 public void removeSeriesByCalculatorName(String calcName) {
   List<AbstractSeries> activeSeries = chart.getAllSeries();
   for (AbstractSeries series : activeSeries) {
     if (series instanceof BaseCounterSeries) {
       String name = ((BaseCounterSeries) series).getCalculator().getName();
       if (name.equals(calcName)) {
         chart.removeSeries(series);
       }
     }
   }
 }
 /**
  * Remove all series associated with the class specified
  *
  * @param toRemove
  */
 public void removeSeriesByClass(Class toRemove) {
   List<AbstractSeries> activeSeries = chart.getAllSeries();
   for (AbstractSeries series : activeSeries) {
     if (series instanceof BaseCounterSeries) {
       BaseCounter bc = ((BaseCounterSeries) series).getCalculator();
       if (bc.getClass() == toRemove) {
         chart.removeSeries(series);
       }
     }
   }
 }
  /**
   * Selects the columns in the associated sequence group that correspond to the selected range in
   * the current chart.
   */
  protected void selectSitesFromAxesRange() {
    if (chart.isRangeSelected()) {
      double[] range = chart.getRangeSelection();
      int rangeStart = (int) Math.round(range[0]);
      int rangeEnd = (int) Math.round(range[1]);

      try {
        SGContentPanelDisplay sgDisplay = (SGContentPanelDisplay) source;
        sgDisplay.selectColumns(rangeStart, rangeEnd);
      } catch (ClassCastException ex) {
        //	apparently source wasn't an sgDisplay, not sure what should happen here
      }
    }
  }
  /**
   * Uses the calculator with name calcName to construct & display a series for the sg. This should
   * be the only way new series' are added to the chart, since we need to keep track of all the
   * current series in the sgSeriesMap
   *
   * @param calcName
   * @param sg
   */
  protected void addSeriesForSG(String calcName, SequenceGroup sg) {
    BaseCounter calc = null;
    try {
      calc = sgReg.getBaseCounterInstance(calcName, sg);
    } catch (IllegalArgumentException ex) {
      System.err.println("Could not find base counter of type : " + calcName);
      return;
    }

    List<AbstractSeries> seriesList = sgSeriesMap.get(sg);

    if (seriesList == null) {
      System.out.println(
          "Uh-oh, this sequence group doesn't have an associated list in the sgSeriesMap...");
    }

    // Make a new series from the calculator
    int windowsize = ((Integer) windowSizeSpinner.getValue()).intValue();
    int windowStep = ((Integer) windowStepSpinner.getValue()).intValue();

    if (usePartitionsBox.isSelected()) {
      for (int i = 0; i < sg.getPartitionCount(); i++) {
        BaseCounterSeries ser = calc.getWindowSeries(windowsize, windowStep, i);
        ser.setPartitionIndex(i);
        seriesList.add(ser);
        ser.setName(calc.getName() + " - " + sg.getPartitionKeyForIndex(i));
        if (ser != null && ser.size() > 0) {
          chart.addDataSeries(ser);
          chart.repaint();
        }
      }

    } else {
      BaseCounterSeries ser = calc.getWindowSeries(windowsize, windowStep);
      seriesList.add(ser);
      if (sgSeriesMap.size() > 1) {
        String name = sg.getName();
        if (name == null) {
          ser.setName(calc.getName());
        } else {
          ser.setName(calc.getName() + " - " + sg.getName());
        }
      }
      if (ser != null && ser.size() > 0) {
        chart.addDataSeries(ser);
        chart.repaint();
      }
    }
  }
 public void popupSaveDataAction() {
   JFileChooser saveChart = new JFileChooser();
   int val = saveChart.showSaveDialog(parent);
   if (val == JFileChooser.APPROVE_OPTION) {
     File file = saveChart.getSelectedFile();
     try {
       BufferedWriter writer = new BufferedWriter(new FileWriter(file));
       writer.write("#Chart data for file : " + currentName + "\n");
       Date now = new Date();
       SimpleDateFormat dateFormatter = new SimpleDateFormat("K:mm a, EEE, MMM d, yyyy");
       writer.write("#Created " + dateFormatter.format(now) + "\n");
       writer.write(
           "#Sliding window data, window size : "
               + windowSizeSpinner.getValue()
               + " window step : "
               + windowStepSpinner.getValue()
               + "\n");
       AbstractSeries data = chart.getSeries(0);
       if (data instanceof XYSeries) {
         XYSeries xyData = (XYSeries) data;
         for (int i = 0; i < data.size(); i++)
           writer.write(xyData.get(i).getX() + ",\t" + xyData.get(i).getY() + "\n");
       } else {
         ErrorWindow.showErrorWindow(
             new IllegalArgumentException(
                 "Cannot write non-xy series data (feature not implemented, yet)"));
       }
       writer.write("\n");
       writer.close();
       logger.info("Wrote data to file : " + file.getAbsolutePath());
     } catch (IOException ioe) {
       logger.warning("Error writing chart data to file : " + ioe.toString());
     }
   }
 }
 /**
  * Clears the all current series from the figure, then re-adds everything based on the current sg
  * list and current calculators list.
  */
 private void repaintAllSeries() {
   chart.removeAllSeriesSilently();
   // System.out.println("Repainting all series!");
   for (String calcName : currentCalculators) {
     for (SequenceGroup sg : sgSeriesMap.keySet()) {
       addSeriesForSG(calcName, sg);
     }
   }
 }
  public void saveChartImage() {
    BufferedImage chartImage = chart.getImage();

    JFileChooser saveChart = new JFileChooser();
    int val = saveChart.showSaveDialog(parent);
    if (val == JFileChooser.APPROVE_OPTION) {
      File file = saveChart.getSelectedFile();
      try {
        ImageIO.write(chartImage, "png", file);
      } catch (IOException ioe) {
        logger.warning("Error saving chart image to file : " + ioe.toString());
      }
    }
  }
  /**
   * This method updates the current list of calculators (currentCalculators) in toggle fashion,
   * meaning that if calcName is in the list, it is removed. If not, it is added. Further, when the
   * calculator is removed, all series in the chart associated with the calculator are also removed.
   * When a calculator is added, series are added for all active sequenceGroups, and all associated
   * partitions if usePartitions is true.
   *
   * @param calcName
   */
  public void toggleCalculator(String calcName) {
    if (currentCalculators.contains(calcName)) {
      currentCalculators.remove(calcName);
      removeSeriesByCalculatorName(calcName);
    } else {
      currentCalculators.add(calcName);

      // For all active sequence groups, add the new data series

      for (SequenceGroup sg : sgSeriesMap.keySet()) {
        addSeriesForSG(calcName, sg);
      }
    }

    chart.inferBoundsFromCurrentSeries();
    repaint();
  }
  public void analyze(String name, Object data) {
    if (data.getClass() != SequenceGroup.class) {
      ErrorWindow.showErrorWindow(
          new IllegalArgumentException(
              "Got a incompatible data type for SequenceLineChart: " + data.getClass()));
      return;
    }

    SequenceGroup currentSG = (SequenceGroup) data;
    originalSG = currentSG;
    originalSG.addPartitionListener(this);
    originalSG.addSGChangeListener(this);
    sgSeriesMap.put((SequenceGroup) data, new ArrayList<AbstractSeries>());
    currentName = name;
    if (topLabel != null) topLabel.setText(currentName);
    else topLabel = new JLabel(currentName);

    topLabel.setFont(defaultFont);

    int maxLength = currentSG.getMaxSeqLength();

    // Guess some decent initial values for size of window and step
    int windowSize = Math.min(100, maxLength);
    int windowStep = maxLength > 50 ? 10 : 1;
    if (maxLength > 9999) {
      windowSize = 1000;
      windowStep = 100;
    }

    SpinnerModel sizeModel = new SpinnerNumberModel(windowSize, 1, maxLength, 1);
    SpinnerModel stepModel = new SpinnerNumberModel(windowStep, 1, maxLength / 2, 1);

    windowSizeSpinner.setModel(sizeModel);
    windowStepSpinner.setModel(stepModel);

    if (currentSG.getPartitionCount() < 2) {
      usePartitionsBox.setSelected(false);
      usePartitionsBox.setEnabled(false);
    } else {
      usePartitionsBox.setEnabled(true);
      usePartitionsBox.setSelected(true);
    }

    toggleCalculator(SGStatisticsRegistry.NUC_DIVERSITY);
    chart.repaint();
  }
  private void initializeComponents() {
    int leftPanelWidth = 250;
    JPanel parentPanel = new JPanel();
    parent = new JScrollPane(parentPanel);
    chartSize = new Dimension(500, 300);
    parentPanel.setLayout(new BoxLayout(parentPanel, BoxLayout.X_AXIS));
    JPanel leftPanel = new JPanel();
    leftPanel.setMaximumSize(new Dimension(leftPanelWidth, Integer.MAX_VALUE));
    parent.setBorder(BorderFactory.createEmptyBorder());
    parent.setViewportBorder(BorderFactory.createEmptyBorder());

    jSeparator1 = new javax.swing.JSeparator();
    chartTypeLabel = new javax.swing.JLabel();
    windowSizeSpinner = new javax.swing.JSpinner();
    windowStepSpinner = new javax.swing.JSpinner();
    jLabel1 = new javax.swing.JLabel();
    jLabel2 = new javax.swing.JLabel();

    leftPanel.setPreferredSize(new java.awt.Dimension(leftPanelWidth, 300));
    leftPanel.setBackground(Color.WHITE);

    leftPanel.setLayout(new BoxLayout(leftPanel, BoxLayout.PAGE_AXIS));

    topLabel = new JLabel();
    topLabel.setText(currentName);
    topLabel.setHorizontalTextPosition(SwingConstants.CENTER);
    leftPanel.add(topLabel);

    JPanel topPanel = new JPanel();
    topPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 1, 0));
    topPanel.setBackground(Color.white);
    topPanel.setMaximumSize(new Dimension(leftPanelWidth, 16));

    topPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
    leftPanel.add(topPanel);

    JPanel buttonPanel = new JPanel();
    buttonPanel.setOpaque(false);
    buttonPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
    buttonPanel.setAlignmentX(LEFT_ALIGNMENT);
    buttonPanel.setMaximumSize(new Dimension(leftPanelWidth, 40));
    buttonPanel.setPreferredSize(new Dimension(leftPanelWidth, 36));

    saveButton = new JButton(getIcon("icons/save.png"));
    saveButton.setFont(defaultFont);
    saveButton.setToolTipText("Save chart image");
    saveButton.addActionListener(
        new ActionListener() {
          public void actionPerformed(ActionEvent arg0) {
            saveChartImage();
          }
        });
    saveButton.setAlignmentX(LEFT_ALIGNMENT);

    JButton restoreButton = new JButton(getIcon("icons/expand.png"));
    restoreButton.setToolTipText("Restore default axes boundaries");
    restoreButton.setFont(defaultFont);
    restoreButton.addActionListener(
        new ActionListener() {
          public void actionPerformed(ActionEvent arg0) {
            restoreDefaultBounds();
          }
        });
    restoreButton.setAlignmentX(LEFT_ALIGNMENT);

    JButton zoomButton = new JButton(getIcon("icons/zoom.png"));
    zoomButton.setToolTipText("Zoom to selection region");
    zoomButton.setFont(defaultFont);
    zoomButton.addActionListener(
        new ActionListener() {
          public void actionPerformed(ActionEvent arg0) {
            zoomToSelection();
          }
        });
    zoomButton.setAlignmentX(LEFT_ALIGNMENT);

    JButton selectRangeButton = new JButton(getIcon("icons/selectColumns.png"));
    selectRangeButton.setFont(defaultFont);
    selectRangeButton.setToolTipText("Select range in sequence group");
    selectRangeButton.setAlignmentX(LEFT_ALIGNMENT);
    selectRangeButton.addActionListener(
        new ActionListener() {
          public void actionPerformed(ActionEvent arg0) {
            selectSitesFromAxesRange();
          }
        });

    JButton clearButton = new JButton("Clear");
    clearButton.setFont(defaultFont);
    // selectRangeButton.setPreferredSize(new Dimension(80, 24));
    clearButton.setAlignmentX(LEFT_ALIGNMENT);
    clearButton.addActionListener(
        new ActionListener() {
          public void actionPerformed(ActionEvent arg0) {
            chart.clearRangeSelection();
            chart.repaint();
          }
        });

    buttonPanel.add(saveButton);
    buttonPanel.add(restoreButton);
    buttonPanel.add(zoomButton);
    buttonPanel.add(selectRangeButton);
    leftPanel.add(buttonPanel);

    usePartitionsBox = new JCheckBox("Use partition data");
    usePartitionsBox.setFont(defaultFont);
    usePartitionsBox.setSelected(true);
    usePartitionsBox.addActionListener(
        new ActionListener() {
          public void actionPerformed(ActionEvent e) {
            repaintAllSeries();
          }
        });

    leftPanel.add(usePartitionsBox);

    SpinnerModel sizeModel = new SpinnerNumberModel(100, 1, 1000, 10);
    SpinnerModel stepModel = new SpinnerNumberModel(100, 1, 1000, 5);
    windowSizeSpinner = new JSpinner(sizeModel);
    windowStepSpinner = new JSpinner(stepModel);
    windowStepSpinner.setMaximumSize(new Dimension(80, 25));
    windowSizeSpinner.setMaximumSize(new Dimension(80, 25));
    windowSizeLabel = new JLabel();
    windowSizeLabel.setFont(defaultFont);
    windowSizeLabel.setText("Window size");
    windowStepLabel = new JLabel();
    windowStepLabel.setFont(defaultFont);
    windowStepLabel.setText("Window step");

    JPanel spinnerLabels = new JPanel();
    spinnerLabels.setAlignmentX(LEFT_ALIGNMENT);
    spinnerLabels.setOpaque(false);
    spinnerLabels.setLayout(new BoxLayout(spinnerLabels, BoxLayout.X_AXIS));
    spinnerLabels.add(windowSizeLabel);
    spinnerLabels.add(Box.createHorizontalStrut(16));
    spinnerLabels.add(windowStepLabel);
    spinnerLabels.add(Box.createHorizontalGlue());

    JPanel spinnerPanel = new JPanel();
    spinnerPanel.setAlignmentX(LEFT_ALIGNMENT);
    spinnerPanel.setOpaque(false);
    spinnerPanel.setLayout(new BoxLayout(spinnerPanel, BoxLayout.X_AXIS));
    spinnerPanel.add(windowSizeSpinner);
    spinnerPanel.add(Box.createHorizontalStrut(25));
    spinnerPanel.add(windowStepSpinner);
    spinnerPanel.add(Box.createHorizontalGlue());
    leftPanel.add(spinnerLabels);
    leftPanel.add(spinnerPanel);

    windowSizeSpinner.setFont(defaultFont);
    windowSizeSpinner.addChangeListener(
        new javax.swing.event.ChangeListener() {
          public void stateChanged(javax.swing.event.ChangeEvent evt) {
            newWindowSizeAction();
          }
        });
    windowStepSpinner.setFont(defaultFont);
    windowStepSpinner.addChangeListener(
        new javax.swing.event.ChangeListener() {
          public void stateChanged(javax.swing.event.ChangeEvent evt) {
            newWindowSizeAction();
          }
        });

    leftPanel.add(Box.createRigidArea(new Dimension(0, 10)));
    JScrollPane pane = createStatisticListPanel();
    pane.setPreferredSize(new Dimension(leftPanelWidth, 200));
    pane.setMaximumSize(new Dimension(leftPanelWidth, 400));
    pane.setBackground(Color.white);
    pane.setAlignmentX(Component.LEFT_ALIGNMENT);
    leftPanel.add(pane);

    leftPanel.add(Box.createVerticalGlue());

    JPanel bottomPanel = new JPanel();
    bottomPanel.setBackground(Color.white);

    chart = new XYSeriesFigure();
    chart.setMinimumSize(chartSize);
    chart.setPreferredSize(chartSize);
    chart.addMouseListener(new PopupListener());

    parentPanel.add(leftPanel);
    parentPanel.add(chart);

    chartPopup = new JPopupMenu();

    JMenuItem popupExport = new JMenuItem("Save as image");
    popupExport.addActionListener(
        new java.awt.event.ActionListener() {
          public void actionPerformed(java.awt.event.ActionEvent evt) {
            saveChartImage();
          }
        });
    chartPopup.add(popupExport);

    JMenuItem popupSaveData = new JMenuItem("Save data as .csv");
    popupSaveData.addActionListener(
        new java.awt.event.ActionListener() {
          public void actionPerformed(java.awt.event.ActionEvent evt) {
            popupSaveDataAction();
          }
        });
    chartPopup.add(popupSaveData);
    chartPopup.setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY));

    setLayout(new BorderLayout());
    add(parent, BorderLayout.CENTER);

    chart.addSeriesListener(this);
  }
 /** Re-infer the axes bounds from the current series & repaint */
 protected void restoreDefaultBounds() {
   chart.inferBoundsFromCurrentSeries();
   repaint();
 }