// Copyright 2004-2014 Jim Voris
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package com.qumasoft.guitools.merge;
import com.qumasoft.qvcslib.QVCSRuntimeException;
import com.qumasoft.qvcslib.QumaAssert;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.ImageIcon;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.ListCellRenderer;
import javax.swing.SwingConstants;
import javax.swing.border.EmptyBorder;
/**
* Descendent file contents list.
* @author Jim Voris
*/
public class DescendentFileContentsList extends JList<MergedDescendentFileContentRow> {
private static final long serialVersionUID = 6832787751335861590L;
private static final int NUMBER_WIDTH = 70;
private final ImageIcon emptyIcon;
private final ImageIcon currentDiffMarkerIcon;
private int maximumContentWidth = 0;
private int height;
private final int fileIndex;
private final CellRenderer cellRenderer;
private final MergeFrame parentFrame;
DescendentFileContentsList(DescendentFileContentsListModel model, int fileIdx, MergeFrame pFrame, Font font) {
super(model);
this.currentDiffMarkerIcon = new ImageIcon(ClassLoader.getSystemResource("images/RedTriRight.png"));
this.emptyIcon = new ImageIcon(ClassLoader.getSystemResource("images/ClearTriRight.png"));
this.parentFrame = pFrame;
this.fileIndex = fileIdx;
this.cellRenderer = new CellRenderer(this);
addMouseListener(new MouseClickListener(this));
setFont(font);
}
@Override
public ListCellRenderer getCellRenderer() {
return cellRenderer;
}
int getRowHeight() {
return height;
}
class CellRenderer extends JPanel implements ListCellRenderer {
private static final long serialVersionUID = -6337963574924920715L;
private MergedDescendentFileContentRow mergedDescendentContentRow;
private final JLabel lineNumber;
private final JList list;
private final JCheckBox privateDoNothingCheckBox;
CellRenderer(JList lst) {
super();
this.privateDoNothingCheckBox = new JCheckBox();
this.lineNumber = new JLabel();
list = lst;
setLayout(new BorderLayout(0, 0));
lineNumber.setForeground(ColorManager.getNormalColor());
lineNumber.setHorizontalTextPosition(SwingConstants.RIGHT);
lineNumber.setHorizontalAlignment(SwingConstants.RIGHT);
// <editor-fold>
height = list.getFont().getSize() + 5;
// </editor-fold>
lineNumber.setPreferredSize(new Dimension(NUMBER_WIDTH, height));
lineNumber.setOpaque(true);
privateDoNothingCheckBox.setEnabled(true);
privateDoNothingCheckBox.setVisible(false);
setOpaque(true);
}
@Override
public java.awt.Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
JPanel lineNumberPanel = new JPanel();
lineNumberPanel.setBorder(new EmptyBorder(0, 0, 0, 10));
lineNumberPanel.setLayout(new BorderLayout(0, 0));
JPanel checkBoxPanel = new JPanel();
checkBoxPanel.setLayout(new GridLayout(1, 1, 0, 0));
Color bkColor = null;
Font font = list.getFont();
boolean showOverlapFlag = false;
boolean showDeletedFlag = false;
removeAll();
mergedDescendentContentRow = getModel().getElementAt(index);
mergedDescendentContentRow.setRowDeletedFlag(false);
mergedDescendentContentRow.setForeground(list.getForeground());
mergedDescendentContentRow.setBackground(list.getBackground());
EditInfo editInfo = null;
int currentWidth;
switch (fileIndex) {
case 0: // Working on the merged result (a.k.a the ancestor file).
lineNumber.setText("");
if ((mergedDescendentContentRow.getFirstDecendentEditInfo() != null)
&& (mergedDescendentContentRow.getFirstDecendentEditInfo().getCheckBox().isSelected())) {
if (mergedDescendentContentRow.getFirstDecendentRowType() == MergedDescendentFileContentRow.ROWTYPE_DELETE) {
mergedDescendentContentRow.setText("");
bkColor = ColorManager.getFirstDecendentChangeBackgroundColor();
} else {
QumaAssert.isTrue(mergedDescendentContentRow.getFirstDecendentRowType() == MergedDescendentFileContentRow.ROWTYPE_INSERT);
mergedDescendentContentRow.setText(mergedDescendentContentRow.getFirstDecendentText());
lineNumber.setText(Integer.toString(mergedDescendentContentRow.getAncestorLineNumber()));
bkColor = ColorManager.getFirstDecendentChangeBackgroundColor();
}
} else if ((mergedDescendentContentRow.getSecondDecendentEditInfo() != null)
&& (mergedDescendentContentRow.getSecondDecendentEditInfo().getCheckBox().isSelected())) {
if (mergedDescendentContentRow.getSecondDecendentRowType() == MergedDescendentFileContentRow.ROWTYPE_DELETE) {
mergedDescendentContentRow.setText("");
bkColor = ColorManager.getSecondDecendentChangeBackgroundColor();
} else {
QumaAssert.isTrue(mergedDescendentContentRow.getSecondDecendentRowType() == MergedDescendentFileContentRow.ROWTYPE_INSERT);
mergedDescendentContentRow.setText(mergedDescendentContentRow.getSecondDecendentText());
lineNumber.setText(Integer.toString(mergedDescendentContentRow.getAncestorLineNumber()));
bkColor = ColorManager.getSecondDecendentChangeBackgroundColor();
}
} else {
mergedDescendentContentRow.setText(mergedDescendentContentRow.getAncestorText());
if (mergedDescendentContentRow.getAncestorLineNumber() != 0) {
lineNumber.setText(Integer.toString(mergedDescendentContentRow.getAncestorLineNumber()));
}
if ((mergedDescendentContentRow.getFirstDecendentEditInfo() != null) || (mergedDescendentContentRow.getSecondDecendentEditInfo() != null)) {
bkColor = ColorManager.getChangeBackgroundColor();
} else {
bkColor = list.getBackground();
}
}
break;
case 1: // Working on the first decendent file...
if (mergedDescendentContentRow.getFirstDecendentRowType() == MergedDescendentFileContentRow.ROWTYPE_DELETE) {
mergedDescendentContentRow.setForeground(ColorManager.getDeleteForegroundColor());
bkColor = ColorManager.getChangeBackgroundColor();
mergedDescendentContentRow.setRowDeletedFlag(true);
showDeletedFlag = true;
} else if (mergedDescendentContentRow.getFirstDecendentRowType() == MergedDescendentFileContentRow.ROWTYPE_REPLACE) {
mergedDescendentContentRow.setForeground(ColorManager.getReplaceForegroundColor());
bkColor = ColorManager.getChangeBackgroundColor();
} else if (mergedDescendentContentRow.getFirstDecendentRowType() == MergedDescendentFileContentRow.ROWTYPE_INSERT) {
mergedDescendentContentRow.setForeground(ColorManager.getInsertForegroundColor());
bkColor = ColorManager.getChangeBackgroundColor();
} else if (mergedDescendentContentRow.getFirstDecendentRowType() == MergedDescendentFileContentRow.ROWTYPE_UNDEFINED) {
mergedDescendentContentRow.setFirstDecendentText("Undefined");
mergedDescendentContentRow.setForeground(ColorManager.getInsertForegroundColor());
bkColor = ColorManager.getChangeBackgroundColor();
} else {
mergedDescendentContentRow.setForeground(ColorManager.getNormalColor());
bkColor = list.getBackground();
}
mergedDescendentContentRow.setText(mergedDescendentContentRow.getFirstDecendentText());
currentWidth = mergedDescendentContentRow.getFontMetrics(mergedDescendentContentRow.getFont()).stringWidth(mergedDescendentContentRow.getFirstDecendentText());
if (currentWidth > maximumContentWidth) {
maximumContentWidth = currentWidth;
}
if (mergedDescendentContentRow.getFirstDecendentLineNumber() == 0) {
lineNumber.setText("");
bkColor = ColorManager.getChangeBackgroundColor();
} else {
lineNumber.setText(Integer.toString(mergedDescendentContentRow.getFirstDecendentLineNumber()));
}
showOverlapFlag = mergedDescendentContentRow.getOverlapFlag();
editInfo = mergedDescendentContentRow.getFirstDecendentEditInfo();
break;
case 2: // Working on the second decendent file...
if (mergedDescendentContentRow.getSecondDecendentRowType() == MergedDescendentFileContentRow.ROWTYPE_DELETE) {
mergedDescendentContentRow.setForeground(ColorManager.getDeleteForegroundColor());
bkColor = ColorManager.getChangeBackgroundColor();
mergedDescendentContentRow.setRowDeletedFlag(true);
showDeletedFlag = true;
} else if (mergedDescendentContentRow.getSecondDecendentRowType() == MergedDescendentFileContentRow.ROWTYPE_REPLACE) {
mergedDescendentContentRow.setForeground(ColorManager.getReplaceForegroundColor());
bkColor = ColorManager.getChangeBackgroundColor();
} else if (mergedDescendentContentRow.getSecondDecendentRowType() == MergedDescendentFileContentRow.ROWTYPE_INSERT) {
mergedDescendentContentRow.setForeground(ColorManager.getInsertForegroundColor());
bkColor = ColorManager.getChangeBackgroundColor();
} else if (mergedDescendentContentRow.getSecondDecendentRowType() == MergedDescendentFileContentRow.ROWTYPE_UNDEFINED) {
mergedDescendentContentRow.setSecondDecendentText("Undefined");
mergedDescendentContentRow.setForeground(ColorManager.getInsertForegroundColor());
bkColor = ColorManager.getChangeBackgroundColor();
} else {
mergedDescendentContentRow.setForeground(ColorManager.getNormalColor());
bkColor = list.getBackground();
}
if (mergedDescendentContentRow.getSecondDecendentText() != null) {
mergedDescendentContentRow.setText(mergedDescendentContentRow.getSecondDecendentText());
} else {
mergedDescendentContentRow.setSecondDecendentText("second decendent text was null");
mergedDescendentContentRow.setText("second decendent text was null");
}
currentWidth = mergedDescendentContentRow.getFontMetrics(mergedDescendentContentRow.getFont()).stringWidth(mergedDescendentContentRow.getSecondDecendentText());
if (currentWidth > maximumContentWidth) {
maximumContentWidth = currentWidth;
}
if (mergedDescendentContentRow.getSecondDecendentLineNumber() == 0) {
lineNumber.setText("");
bkColor = ColorManager.getChangeBackgroundColor();
} else {
lineNumber.setText(Integer.toString(mergedDescendentContentRow.getSecondDecendentLineNumber()));
}
showOverlapFlag = mergedDescendentContentRow.getOverlapFlag();
editInfo = mergedDescendentContentRow.getSecondDecendentEditInfo();
break;
default:
throw new QVCSRuntimeException("Internal error -- illegal file index");
}
if (index == parentFrame.getCurrentRowIndex()) {
lineNumber.setIcon(currentDiffMarkerIcon);
} else {
lineNumber.setIcon(emptyIcon);
}
if (fileIndex > 0 && editInfo != null) {
switch (fileIndex) {
case 1:
if (mergedDescendentContentRow.getFirstDecendentCheckBoxVisibleFlag()) {
editInfo.getCheckBox().setVisible(true);
editInfo.getCheckBox().setEnabled(true);
checkBoxPanel.add(editInfo.getCheckBox());
} else {
checkBoxPanel.add(privateDoNothingCheckBox);
}
break;
case 2:
if (mergedDescendentContentRow.getSecondDecendentCheckBoxVisibleFlag()) {
editInfo.getCheckBox().setVisible(true);
editInfo.getCheckBox().setEnabled(true);
checkBoxPanel.add(editInfo.getCheckBox());
} else {
checkBoxPanel.add(privateDoNothingCheckBox);
}
break;
default:
throw new QVCSRuntimeException("Internal error -- illegal file index");
}
} else {
checkBoxPanel.add(privateDoNothingCheckBox);
}
lineNumberPanel.add(BorderLayout.WEST, checkBoxPanel);
lineNumberPanel.add(BorderLayout.CENTER, lineNumber);
add(BorderLayout.WEST, lineNumberPanel);
add(BorderLayout.CENTER, mergedDescendentContentRow);
if (isSelected) {
mergedDescendentContentRow.setBackground(list.getSelectionBackground());
mergedDescendentContentRow.setForeground(list.getSelectionForeground());
mergedDescendentContentRow.setIsSelectedFlag(true);
checkBoxPanel.setBackground(list.getSelectionBackground());
checkBoxPanel.setForeground(list.getSelectionForeground());
lineNumberPanel.setBackground(list.getSelectionBackground());
lineNumberPanel.setForeground(list.getSelectionForeground());
lineNumber.setBackground(list.getSelectionBackground());
lineNumber.setForeground(list.getSelectionForeground());
} else {
mergedDescendentContentRow.setIsSelectedFlag(false);
mergedDescendentContentRow.setBackground(bkColor);
checkBoxPanel.setForeground(list.getForeground());
lineNumberPanel.setForeground(list.getForeground());
lineNumber.setForeground(list.getForeground());
if (showOverlapFlag) {
checkBoxPanel.setBackground(ColorManager.getCollisionBackgroundColor());
lineNumberPanel.setBackground(ColorManager.getCollisionBackgroundColor());
lineNumber.setBackground(ColorManager.getCollisionBackgroundColor());
} else if (showDeletedFlag) {
checkBoxPanel.setBackground(ColorManager.getDeletedRowLineNumberBackgroundColor());
lineNumberPanel.setBackground(ColorManager.getDeletedRowLineNumberBackgroundColor());
lineNumber.setBackground(ColorManager.getDeletedRowLineNumberBackgroundColor());
} else {
checkBoxPanel.setBackground(ColorManager.getChangeBackgroundColor());
lineNumberPanel.setBackground(ColorManager.getChangeBackgroundColor());
lineNumber.setBackground(ColorManager.getChangeBackgroundColor());
}
}
mergedDescendentContentRow.setEnabled(list.isEnabled());
mergedDescendentContentRow.setFont(font);
mergedDescendentContentRow.setOpaque(true);
lineNumber.setFont(list.getFont());
return this;
}
@Override
public int getWidth() {
int width = lineNumber.getWidth() + mergedDescendentContentRow.getWidth();
return width;
}
@Override
public Dimension getPreferredSize() {
Dimension preferredSize = new Dimension(NUMBER_WIDTH + maximumContentWidth, height);
return preferredSize;
}
}
class MouseClickListener extends MouseAdapter {
private final JList list;
MouseClickListener(JList l) {
list = l;
}
@Override
public void mouseClicked(MouseEvent e) {
// We need to check for overlap and prevent two checkboxes from
// being turned on in the case of overlap.
int index = list.locationToIndex(e.getPoint());
MergedDescendentFileContentRow row = getModel().getElementAt(index);
switch (fileIndex) {
case 1:
if (row.getFirstDecendentEditInfo() != null) {
EditInfo editInfo = row.getFirstDecendentEditInfo();
if (editInfo.getCheckBox().isSelected()) {
editInfo.getCheckBox().setSelected(false);
} else {
if (isOverLapDetected(row) == false) {
editInfo.getCheckBox().setSelected(true);
} else {
JOptionPane.showMessageDialog(parentFrame, "In an overlap region, you can choose an edit from only one file!", "Collision Area!", JOptionPane.INFORMATION_MESSAGE);
}
}
parentFrame.reNumberMergedResult();
parentFrame.repaint();
}
break;
case 2:
if (row.getSecondDecendentEditInfo() != null) {
EditInfo editInfo = row.getSecondDecendentEditInfo();
if (editInfo.getCheckBox().isSelected()) {
editInfo.getCheckBox().setSelected(false);
} else {
if (isOverLapDetected(row) == false) {
editInfo.getCheckBox().setSelected(true);
} else {
JOptionPane.showMessageDialog(parentFrame, "In an overlap region, you can choose an edit from only one file!", "Collision Area!", JOptionPane.INFORMATION_MESSAGE);
}
}
parentFrame.reNumberMergedResult();
parentFrame.repaint();
}
break;
default:
throw new QVCSRuntimeException("Internal error -- illegal file index");
}
}
/**
* Return true if there is overlap and the other panel's checkbox is already enabled.
*
* @param row
* @return true if the checkbox for a colliding edit is selected.
*/
private boolean isOverLapDetected(MergedDescendentFileContentRow row) {
boolean retVal = false;
switch (fileIndex) {
case 1:
// They clicked the checkbox on file1. Only allow the check
// to be enabled if there is no overlap.
if (row.getSecondDecendentEditInfo() != null) {
if (row.getSecondDecendentEditInfo().getCheckBox().isSelected()) {
retVal = true;
}
}
break;
case 2:
// They clicked the checkbox on the second decendent. Only
// allow the checkbox be enabled if there is no overlap.
if (row.getFirstDecendentEditInfo() != null) {
if (row.getFirstDecendentEditInfo().getCheckBox().isSelected()) {
retVal = true;
}
}
break;
default:
throw new QVCSRuntimeException("Internal error -- illegal file index");
}
return retVal;
}
}
}