/*
* (c) Copyright 2010-2011 AgileBirds
*
* This file is part of OpenFlexo.
*
* OpenFlexo 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.
*
* OpenFlexo 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 OpenFlexo. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.openflexo.swing.diff;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.DefaultListCellRenderer;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ListSelectionModel;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import org.openflexo.diff.ComputeDiff;
import org.openflexo.diff.ComputeDiff.AdditionChange;
import org.openflexo.diff.ComputeDiff.DiffChange;
import org.openflexo.diff.ComputeDiff.DiffReport;
import org.openflexo.diff.ComputeDiff.ModificationChange;
import org.openflexo.diff.ComputeDiff.RemovalChange;
import org.openflexo.icon.UtilsIconLibrary;
import org.openflexo.toolbox.TokenMarkerStyle;
public class DiffPanel extends JLayeredPane {
// This flag is used to get either left perspective or right perspective
boolean isLeftOriented = true;
private DiffReport _report;
DiffTextArea _leftTextArea;
DiffTextArea _rightTextArea;
JList changesList;
private ListSelectionListener lsl;
private String NO_CHANGES_LABEL = "No structural changes";
static Icon iconForChange(DiffChange value, boolean isLeftOriented) {
if (value instanceof ModificationChange) {
return isLeftOriented ? UtilsIconLibrary.MODIFICATION_LEFT_ICON : UtilsIconLibrary.MODIFICATION_RIGHT_ICON;
} else if (value instanceof AdditionChange) {
return isLeftOriented ? UtilsIconLibrary.REMOVAL_LEFT_ICON : UtilsIconLibrary.ADDITION_RIGHT_ICON;
} else if (value instanceof RemovalChange) {
return isLeftOriented ? UtilsIconLibrary.ADDITION_LEFT_ICON : UtilsIconLibrary.REMOVAL_RIGHT_ICON;
}
return null;
}
public DiffPanel(DiffReport diffReport) {
this(diffReport, TokenMarkerStyle.None);
}
public DiffPanel(DiffReport diffReport, boolean _isLeftOriented) {
this(diffReport, TokenMarkerStyle.None, _isLeftOriented);
}
public DiffPanel(DiffReport diffReport, TokenMarkerStyle style) {
this(diffReport, style, null, null, null, true, BorderLayout.NORTH);
}
public DiffPanel(DiffReport diffReport, TokenMarkerStyle style, boolean _isLeftOriented) {
this(diffReport, style, null, null, null, _isLeftOriented, BorderLayout.NORTH);
}
public DiffPanel(DiffReport diffReport, TokenMarkerStyle style, String changesListOrientation) {
this(diffReport, style, null, null, null, true, changesListOrientation);
}
public DiffPanel(DiffReport diffReport, TokenMarkerStyle style, String leftLabel, String rightLabel, String noChangeLabel,
boolean _isLeftOriented) {
this(diffReport, style, leftLabel, rightLabel, noChangeLabel, _isLeftOriented, BorderLayout.NORTH);
}
public DiffPanel(DiffReport diffReport, TokenMarkerStyle style, String leftLabel, String rightLabel, String noChangeLabel,
boolean _isLeftOriented, String changesListOrientation) {
super();
_report = diffReport;
setLayout(new BorderLayout());
this.isLeftOriented = _isLeftOriented;
JPanel topPanel = new JPanel(new BorderLayout());
// JLabel title = new JLabel("Diff panel",JLabel.CENTER);
if (_report.getChanges().size() > 0) {
changesList = new JList(_report.getChanges());
changesList.setVisibleRowCount(5);
changesList.setCellRenderer(new DefaultListCellRenderer() {
@Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
JLabel returned = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
returned.setIcon(iconForChange((DiffChange) value, DiffPanel.this.isLeftOriented));
returned.setText(((DiffChange) value).toNiceString(DiffPanel.this.isLeftOriented));
return returned;
}
});
changesList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
lsl = new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
selectChange((ComputeDiff.DiffChange) changesList.getSelectedValue(), false);
}
};
changesList.addListSelectionListener(lsl);
changesList.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
super.mouseClicked(e);
if (e.getClickCount() == 2) {
// When double-clicked, scroll to selected change
selectChange((ComputeDiff.DiffChange) changesList.getSelectedValue(), true);
}
}
});
} else {
if (noChangeLabel != null) {
NO_CHANGES_LABEL = noChangeLabel;
}
Object[] obj = { NO_CHANGES_LABEL };
changesList = new JList(obj);
changesList.setEnabled(false);
changesList.setVisibleRowCount(1);
}
// topPanel.add(title,BorderLayout.NORTH);
topPanel.add(new JScrollPane(changesList), BorderLayout.CENTER);
if (leftLabel != null || rightLabel != null) {
JPanel labelPanels = new JPanel(new GridLayout(1, 2));
JLabel _leftLabel = new JLabel(leftLabel, SwingConstants.CENTER);
JLabel _rightLabel = new JLabel(rightLabel, SwingConstants.CENTER);
labelPanels.add(_leftLabel);
labelPanels.add(_rightLabel);
labelPanels.validate();
topPanel.add(labelPanels, BorderLayout.SOUTH);
}
topPanel.validate();
_leftTextArea = new DiffTextArea(_report.getInput0(), diffReport, DiffTextArea.Side.Left, style, isLeftOriented);
_rightTextArea = new DiffTextArea(_report.getInput1(), diffReport, DiffTextArea.Side.Right, style, isLeftOriented);
JComponent separator = getSeparator();
JComponent left = _leftTextArea;
JComponent right = _rightTextArea;
GridBagLayout gridbag = new GridBagLayout();
GridBagConstraints c = new GridBagConstraints();
JPanel centerPane = new JPanel();
centerPane.setLayout(gridbag);
// c.gridwidth = 1;
// c.gridheight = 1;
c.weightx = 0.0;
c.anchor = GridBagConstraints.NORTHWEST;
c.fill = GridBagConstraints.NONE;
// c.gridwidth = 1;
gridbag.setConstraints(_leftTextArea.getLinesTA(), c);
centerPane.add(_leftTextArea.getLinesTA());
c.weightx = 1.0;
c.weighty = 1.0;
// c.gridwidth = 1;
// c.gridheight = 1;
c.anchor = GridBagConstraints.WEST;
c.fill = GridBagConstraints.BOTH;
// c.gridwidth = GridBagConstraints.REMAINDER;
gridbag.setConstraints(left, c);
centerPane.add(left);
// c.gridwidth = 1;
// c.gridheight = 1;
c.weightx = 0.0;
c.anchor = GridBagConstraints.CENTER;
c.fill = GridBagConstraints.VERTICAL;
// c.gridwidth = 1;
gridbag.setConstraints(separator, c);
centerPane.add(separator);
c.weightx = 1.0;
c.weighty = 1.0;
// c.gridwidth = 1;
// c.gridheight = 1;
c.anchor = GridBagConstraints.EAST;
c.fill = GridBagConstraints.BOTH;
// c.gridwidth = GridBagConstraints.REMAINDER;
gridbag.setConstraints(right, c);
centerPane.add(right);
// c.gridwidth = 1;
// c.gridheight = 1;
c.weightx = 0.0;
c.anchor = GridBagConstraints.NORTHEAST;
c.fill = GridBagConstraints.NONE;
// c.gridwidth = 1;
gridbag.setConstraints(_rightTextArea.getLinesTA(), c);
centerPane.add(_rightTextArea.getLinesTA());
centerPane.validate();
add(topPanel, changesListOrientation);
JPanel comparePanel = new JPanel(new BorderLayout());
scrollPane = new JScrollPane(centerPane, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
comparePanel.add(scrollPane, BorderLayout.CENTER);
scrollPane.getVerticalScrollBar().setUnitIncrement(10);
scrollPane.getVerticalScrollBar().setBlockIncrement(50);
_leftTextArea.getHorizontalScrollBar().addAdjustmentListener(leftSBAdjustementListener = new AdjustmentListener() {
@Override
public void adjustmentValueChanged(AdjustmentEvent e) {
_rightTextArea.getHorizontalScrollBar().removeAdjustmentListener(rightSBAdjustementListener);
_rightTextArea.getHorizontalScrollBar().setValue(
e.getValue()
* (_rightTextArea.getHorizontalScrollBar().getMaximum()
- _rightTextArea.getHorizontalScrollBar().getVisibleAmount() - _rightTextArea
.getHorizontalScrollBar().getMinimum())
/ (_leftTextArea.getHorizontalScrollBar().getMaximum()
- _leftTextArea.getHorizontalScrollBar().getVisibleAmount() - _leftTextArea
.getHorizontalScrollBar().getMinimum()));
_rightTextArea.getHorizontalScrollBar().addAdjustmentListener(rightSBAdjustementListener);
}
});
_rightTextArea.getHorizontalScrollBar().addAdjustmentListener(rightSBAdjustementListener = new AdjustmentListener() {
@Override
public void adjustmentValueChanged(AdjustmentEvent e) {
_leftTextArea.getHorizontalScrollBar().removeAdjustmentListener(leftSBAdjustementListener);
_leftTextArea.getHorizontalScrollBar().setValue(
e.getValue()
* (_leftTextArea.getHorizontalScrollBar().getMaximum()
- _leftTextArea.getHorizontalScrollBar().getVisibleAmount() - _leftTextArea
.getHorizontalScrollBar().getMinimum())
/ (_rightTextArea.getHorizontalScrollBar().getMaximum()
- _rightTextArea.getHorizontalScrollBar().getVisibleAmount() - _rightTextArea
.getHorizontalScrollBar().getMinimum()));
_leftTextArea.getHorizontalScrollBar().addAdjustmentListener(leftSBAdjustementListener);
}
});
JPanel scrollBars = new JPanel(new GridLayout(1, 2));
scrollBars.add(_leftTextArea.getHorizontalScrollBar());
scrollBars.add(_rightTextArea.getHorizontalScrollBar());
scrollBars.validate();
comparePanel.add(scrollBars, BorderLayout.SOUTH);
comparePanel.validate();
add(comparePanel, BorderLayout.CENTER);
validate();
if (_report.getChanges().size() > 0) {
// selectChange(_report.getChanges().firstElement());
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
changesList.setSelectedIndex(0);
}
});
}
}
private JScrollPane scrollPane;
AdjustmentListener leftSBAdjustementListener;
AdjustmentListener rightSBAdjustementListener;
void selectChange(ComputeDiff.DiffChange change, boolean forceSelect)
{
if ((ComputeDiff.DiffChange) changesList.getSelectedValue() != change) {
changesList.removeListSelectionListener(lsl);
changesList.setSelectedValue(change, true);
changesList.addListSelectionListener(lsl);
}
_leftTextArea.setChange(change, true, forceSelect);
_rightTextArea.setChange(change, true, forceSelect);
}
private JComponent _separator = null;
private JComponent getSeparator() {
if (_separator == null) {
_separator = new JPanel();
_separator.setLayout(new BoxLayout(_separator, BoxLayout.Y_AXIS));
_separator.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5));
}
return _separator;
}
protected boolean buttonsInitialized = false;
private class SelectChange implements ActionListener {
private DiffChange change;
SelectChange(DiffChange change) {
this.change = change;
}
@Override
public void actionPerformed(ActionEvent e) {
selectChange(change, false);
}
}
private void updateChangeButtonLocations() {
if (!buttonsInitialized) {
if (_leftTextArea.readyToDisplay()) {
int remainderHeight = _leftTextArea.getPreferredSize().height;
for (DiffChange c : _report.getChanges()) {
JButton button = new JButton(iconForChange(c, isLeftOriented));
button.addActionListener(new SelectChange(c));
button.setBorder(BorderFactory
.createEmptyBorder(0, 0, 0, _leftTextArea.getVerticalScrollBar().getPreferredSize().width));
int height = _leftTextArea.heightAboveChange(c, button.getPreferredSize().height);
_separator.add(Box.createRigidArea(new Dimension(0, height)));
_separator.add(button);
remainderHeight -= button.getPreferredSize().height;
remainderHeight -= height;
}
_separator.add(Box.createRigidArea(new Dimension(0, remainderHeight)));
buttonsInitialized = true;
} else {
// Not ready
}
}
}
@Override
public void paint(Graphics g) {
super.paint(g);
updateChangeButtonLocations();
}
public DiffTextArea getLeftTextArea() {
return _leftTextArea;
}
public DiffTextArea getRightTextArea() {
return _rightTextArea;
}
public void setFirstVisibleLine(int firstVisibleLine) {
scrollPane.scrollRectToVisible(new Rectangle(100, 100));
}
}