/**
  * This function is used to re-run the analyser, and re-create the rows corresponding the its
  * results.
  */
 private void refreshReviewTable() {
   reviewPanel.removeAll();
   rows.clear();
   GridBagLayout gbl = new GridBagLayout();
   reviewPanel.setLayout(gbl);
   GridBagConstraints gbc = new GridBagConstraints();
   gbc.fill = GridBagConstraints.HORIZONTAL;
   gbc.gridy = 0;
   try {
     Map<String, Long> sums =
         analyser.processLogFile(config.getLogFilename(), fromDate.getDate(), toDate.getDate());
     for (Entry<String, Long> entry : sums.entrySet()) {
       String project = entry.getKey();
       double hours = 1.0 * entry.getValue() / (1000 * 3600);
       addRow(gbl, gbc, project, hours);
     }
     for (String project : main.getProjectsTree().getTopLevelProjects())
       if (!rows.containsKey(project)) addRow(gbl, gbc, project, 0);
     gbc.insets = new Insets(10, 0, 0, 0);
     addLeftLabel(gbl, gbc, "TOTAL");
     gbc.gridx = 1;
     gbc.weightx = 1;
     totalLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 3));
     gbl.setConstraints(totalLabel, gbc);
     reviewPanel.add(totalLabel);
     gbc.weightx = 0;
     addRightLabel(gbl, gbc);
   } catch (IOException e) {
     e.printStackTrace();
   }
   recomputeTotal();
   pack();
 }
 /**
  * Adds a label that shows the percentage of hours spent on a particular project.
  *
  * @param gbl The layout to add the label to.
  * @param gbc The layout constraints to use.
  * @param row The row to link against.
  * @see {@link #addRow(GridBagLayout, GridBagConstraints, String, double)}
  */
 private void addPercentLabel(GridBagLayout gbl, GridBagConstraints gbc, Row row) {
   gbc.gridx = 3;
   gbc.ipadx = 5;
   gbl.setConstraints(row.percentL, gbc);
   gbc.ipadx = 0;
   reviewPanel.add(row.percentL);
 }
 /**
  * Adds a simple 'h' to show that the time period is specified in hours.
  *
  * @param gbl The layout to add the label to.
  * @param gbc The layout constraints to use.
  * @see {@link #addRow(GridBagLayout, GridBagConstraints, String, double)}
  */
 private void addRightLabel(GridBagLayout gbl, GridBagConstraints gbc) {
   JLabel hLabel = new JLabel("h", SwingConstants.CENTER);
   gbc.gridx = 2;
   gbc.ipadx = 5;
   gbl.setConstraints(hLabel, gbc);
   gbc.ipadx = 0;
   reviewPanel.add(hLabel);
 }
 /**
  * Adds an editable text field containing the hours spent on a project.
  *
  * @param gbl The layout to add the text field to.
  * @param gbc The layout constraints to use.
  * @param row The row to link against.
  * @param hours The number of hours spent on the project.
  * @see {@link #addRow(GridBagLayout, GridBagConstraints, String, double)}
  */
 private void addMiddleField(GridBagLayout gbl, GridBagConstraints gbc, Row row, double hours) {
   row.hoursTF.setText(decimalFormat.format(hours));
   gbc.gridx = 1;
   gbc.weightx = 1;
   gbl.setConstraints(row.hoursTF, gbc);
   gbc.weightx = 0;
   reviewPanel.add(row.hoursTF);
 }
 /**
  * Adds the description part of a row.
  *
  * @param gbl The layout to add the label to.
  * @param gbc The layout constraints to use.
  * @param title The title of the top-level project.
  * @see {@link #addRow(GridBagLayout, GridBagConstraints, String, double)}
  */
 private void addLeftLabel(GridBagLayout gbl, GridBagConstraints gbc, String title) {
   JLabel projectLabel = new JLabel(title + ": ", SwingConstants.RIGHT);
   gbc.gridx = 0;
   gbl.setConstraints(projectLabel, gbc);
   reviewPanel.add(projectLabel);
 }
 /**
  * The only constructor of the review dialog, which initialises the dates, sets the outer layout,
  * runs the analyser, creates rows for the results, and displays the window.
  *
  * @param main A link to the parent component.
  * @param config A link to the configuration object.
  */
 ReviewDialog(Main main, Config config) {
   super(main, "Review & Save");
   this.main = main;
   this.config = config;
   // layout date components
   GridBagLayout gbl = new GridBagLayout();
   setLayout(gbl);
   GridBagConstraints gbc = new GridBagConstraints();
   gbc.fill = GridBagConstraints.BOTH;
   gbc.insets = new Insets(0, 0, 5, 0);
   gbc.ipadx = 10;
   gbc.gridx = 0;
   gbc.gridy = 0;
   gbl.setConstraints(yearLabel, gbc);
   gbc.gridx = 1;
   gbl.setConstraints(yearCB, gbc);
   gbc.gridx = 0;
   gbc.gridy = 1;
   gbl.setConstraints(weekLabel, gbc);
   gbc.gridx = 1;
   gbl.setConstraints(weekCB, gbc);
   gbc.gridx = 0;
   gbc.gridy = 2;
   gbl.setConstraints(fromLabel, gbc);
   gbc.weightx = 1;
   gbc.gridx = 1;
   gbl.setConstraints(fromDate, gbc);
   gbc.insets = new Insets(0, 0, 0, 0);
   gbc.weightx = 0;
   gbc.gridx = 0;
   gbc.gridy = 3;
   gbl.setConstraints(toLabel, gbc);
   gbc.weightx = 1;
   gbc.gridx = 1;
   gbl.setConstraints(toDate, gbc);
   gbc.gridx = 0;
   gbc.gridy = 4;
   gbc.gridwidth = 2;
   gbc.insets = new Insets(10, 5, 10, 5);
   gbc.weighty = 1;
   JScrollPane scrollReviewPanel = new JScrollPane(reviewPanel);
   gbl.setConstraints(scrollReviewPanel, gbc);
   gbc.insets = new Insets(0, 0, 0, 0);
   gbc.gridy = 5;
   gbc.weighty = 0;
   gbl.setConstraints(saveToFileButton, gbc);
   gbc.gridy = 6;
   gbl.setConstraints(copyToClipboardButton, gbc);
   add(yearLabel);
   add(yearCB);
   add(weekLabel);
   add(weekCB);
   add(fromLabel);
   add(fromDate);
   add(toLabel);
   add(toDate);
   add(scrollReviewPanel);
   add(saveToFileButton);
   add(copyToClipboardButton);
   // layout results
   updateYearWeekDates();
   setVisible(true);
   setLocation(main.getLocation());
 }