/*
JMeld is a visual diff and merge tool.
Copyright (C) 2007 Kees Kuip
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301 USA
*/
package org.jmeld.ui;
import org.jdesktop.swingworker.SwingWorker;
import org.jdesktop.swingx.decorator.ColorHighlighter;
import org.jdesktop.swingx.decorator.HighlightPredicate;
import org.jdesktop.swingx.treetable.TreeTableNode;
import org.jmeld.JMeldException;
import org.jmeld.settings.EditorSettings;
import org.jmeld.settings.FolderSettings;
import org.jmeld.settings.JMeldSettings;
import org.jmeld.ui.action.ActionHandler;
import org.jmeld.ui.action.MeldAction;
import org.jmeld.ui.swing.table.JMTreeTableModel;
import org.jmeld.ui.util.Colors;
import org.jmeld.ui.util.ImageUtil;
import org.jmeld.ui.util.SwingUtil;
import org.jmeld.util.conf.ConfigurationListenerIF;
import org.jmeld.util.file.FolderDiff;
import org.jmeld.util.file.cmd.AbstractCmd;
import org.jmeld.util.node.JMDiffNode;
import javax.swing.*;
import javax.swing.tree.TreePath;
import javax.swing.undo.CompoundEdit;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.MouseListener;
import java.util.*;
import java.util.List;
public class FolderDiffPanel extends FolderDiffForm implements ConfigurationListenerIF {
protected JMeldPanel mainPanel;
protected FolderDiff diff;
protected ActionHandler actionHandler;
protected JMTreeTableModel treeTableModel;
protected FolderDiffMouseAdapter folderDiffMouseAdapter;
FolderDiffPanel(JMeldPanel mainPanel, FolderDiff diff) {
this.mainPanel = mainPanel;
this.diff = diff;
init();
}
protected void init() {
actionHandler = new ActionHandler();
hierarchyComboBox.setModel(new DefaultComboBoxModel(
FolderSettings.FolderView.values()));
hierarchyComboBox.setSelectedItem(getFolderSettings().getView());
hierarchyComboBox.setFocusable(false);
initActions();
onlyRightButton.setText(null);
onlyRightButton.setIcon(ImageUtil.getImageIcon("jmeld_only-right"));
onlyRightButton.setFocusable(false);
onlyRightButton.setSelected(getFolderSettings().getOnlyRight());
leftRightChangedButton.setText(null);
leftRightChangedButton.setIcon(ImageUtil
.getImageIcon("jmeld_left-right-changed"));
leftRightChangedButton.setFocusable(false);
leftRightChangedButton.setSelected(getFolderSettings()
.getLeftRightChanged());
onlyLeftButton.setText(null);
onlyLeftButton.setIcon(ImageUtil.getImageIcon("jmeld_only-left"));
onlyLeftButton.setFocusable(false);
onlyLeftButton.setSelected(getFolderSettings().getOnlyLeft());
leftRightUnChangedButton.setText(null);
leftRightUnChangedButton.setIcon(ImageUtil
.getImageIcon("jmeld_left-right-unchanged"));
leftRightUnChangedButton.setFocusable(false);
leftRightUnChangedButton.setSelected(getFolderSettings()
.getLeftRightUnChanged());
expandAllButton.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
expandAllButton.setContentAreaFilled(false);
expandAllButton.setText(null);
expandAllButton.setIcon(ImageUtil.getSmallImageIcon("stock_expand-all"));
expandAllButton.setPressedIcon(ImageUtil
.createDarkerIcon((ImageIcon) expandAllButton.getIcon()));
expandAllButton.setFocusable(false);
collapseAllButton.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
collapseAllButton.setContentAreaFilled(false);
collapseAllButton.setText(null);
collapseAllButton
.setIcon(ImageUtil.getSmallImageIcon("stock_collapse-all"));
collapseAllButton.setPressedIcon(ImageUtil
.createDarkerIcon((ImageIcon) collapseAllButton.getIcon()));
collapseAllButton.setFocusable(false);
folder1Label.init();
folder1Label.setText(diff.getLeftFolderName(), diff.getRightFolderName());
folder2Label.init();
folder2Label.setText(diff.getRightFolderName(), diff.getLeftFolderName());
folderTreeTable.setTreeTableModel(getTreeTableModel());
folderTreeTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
folderTreeTable.setToggleClickCount(1);
folderTreeTable.setTerminateEditOnFocusLost(false);
folderTreeTable.setRowSelectionAllowed(true);
folderDiffMouseAdapter = new FolderDiffMouseAdapter(this);
folderTreeTable.addMouseListener(folderDiffMouseAdapter);
folderTreeTable.expandAll();
folderTreeTable.addHighlighter(new ColorHighlighter(
HighlightPredicate.EVEN, Color.white, Color.black));
folderTreeTable.addHighlighter(new ColorHighlighter(HighlightPredicate.ODD,
Colors.getTableRowHighLighterColor(), Color.black));
JMeldSettings.getInstance().addConfigurationListener(this);
}
protected void initActions() {
MeldAction action;
action = actionHandler.createAction(this, mainPanel.actions.FOLDER_SELECT_NEXT_ROW);
installKey("DOWN", action);
action = actionHandler.createAction(this, mainPanel.actions.FOLDER_SELECT_PREVIOUS_ROW);
installKey("UP", action);
action = actionHandler.createAction(this, mainPanel.actions.FOLDER_NEXT_NODE);
installKey("RIGHT", action);
action = actionHandler.createAction(this, mainPanel.actions.FOLDER_PREVIOUS_NODE);
installKey("LEFT", action);
action = actionHandler.createAction(this, mainPanel.actions.FOLDER_OPEN_FILE_COMPARISON);
action.setIcon("stock_compare");
compareButton.setAction(action);
compareButton.setText(null);
compareButton.setFocusable(false);
compareButton.setDisabledIcon(action.getTransparentSmallImageIcon());
installKey("ENTER", action);
action = actionHandler.createAction(this, mainPanel.actions.FOLDER_OPEN_FILE_COMPARISON_BACKGROUND);
action.setIcon("stock_compare");
installKey("alt ENTER", action);
action = actionHandler.createAction(this, mainPanel.actions.FOLDER_EXPAND_ALL);
expandAllButton.setAction(action);
action = actionHandler.createAction(this, mainPanel.actions.FOLDER_COLLAPSE_ALL);
collapseAllButton.setAction(action);
action = actionHandler.createAction(this, mainPanel.actions.FOLDER_REFRESH);
action.setIcon("stock_refresh");
refreshButton.setAction(action);
refreshButton.setText(null);
refreshButton.setFocusable(false);
refreshButton.setDisabledIcon(action.getTransparentSmallImageIcon());
action = actionHandler.createAction(this, mainPanel.actions.FOLDER_REMOVE_RIGHT);
action.setIcon("stock_delete");
deleteRightButton.setAction(action);
deleteRightButton.setText(null);
deleteRightButton.setFocusable(false);
deleteRightButton.setDisabledIcon(action.getTransparentSmallImageIcon());
installKey("ctrl alt RIGHT", action);
installKey("ctrl alt KP_RIGHT", action);
action = actionHandler.createAction(this, mainPanel.actions.FOLDER_REMOVE_LEFT);
action.setIcon("stock_delete");
deleteLeftButton.setAction(action);
deleteLeftButton.setText(null);
deleteLeftButton.setFocusable(false);
deleteLeftButton.setDisabledIcon(action.getTransparentSmallImageIcon());
installKey("ctrl alt LEFT", action);
installKey("ctrl alt KP_LEFT", action);
action = actionHandler.createAction(this, mainPanel.actions.FOLDER_COPY_TO_LEFT);
action.setIcon("stock_left");
copyToLeftButton.setAction(action);
copyToLeftButton.setText(null);
copyToLeftButton.setFocusable(false);
copyToLeftButton.setDisabledIcon(action.getTransparentSmallImageIcon());
installKey("alt LEFT", action);
installKey("alt KP_LEFT", action);
action = actionHandler.createAction(this, mainPanel.actions.FOLDER_COPY_TO_RIGHT);
action.setIcon("stock_right");
copyToRightButton.setAction(action);
copyToRightButton.setText(null);
copyToRightButton.setFocusable(false);
copyToRightButton.setDisabledIcon(action.getTransparentSmallImageIcon());
installKey("alt RIGHT", action);
installKey("alt KP_RIGHT", action);
action = actionHandler.createAction(this, mainPanel.actions.FOLDER_FILTER);
onlyRightButton.setAction(action);
leftRightChangedButton.setAction(action);
onlyLeftButton.setAction(action);
leftRightUnChangedButton.setAction(action);
hierarchyComboBox.setAction(action);
}
private void installKey(String key, MeldAction action) {
SwingUtil.installKey(folderTreeTable, key, action);
}
public String getTitle() {
return diff.getLeftFolderShortName() + " - "
+ diff.getRightFolderShortName();
}
protected TreeTableNode getRootNode() {
return filter(diff.getRootNode());
}
private TreeTableNode filter(JMDiffNode diffNode) {
List<JMDiffNode> nodes;
UINode uiParentNode;
UINode uiNode;
UINode rootNode;
JMDiffNode parent;
Object hierarchy;
// Filter the nodes:
nodes = new ArrayList();
for (JMDiffNode node : diff.getNodes()) {
if (!node.isLeaf()) {
continue;
}
if (node.isCompareEqual(JMDiffNode.Compare.Equal)) {
if (leftRightUnChangedButton.isSelected()) {
nodes.add(node);
}
} else if (node.isCompareEqual(JMDiffNode.Compare.NotEqual)) {
if (leftRightChangedButton.isSelected()) {
nodes.add(node);
}
} else if (node.isCompareEqual(JMDiffNode.Compare.RightMissing)) {
if (onlyLeftButton.isSelected()) {
nodes.add(node);
}
} else if (node.isCompareEqual(JMDiffNode.Compare.LeftMissing)) {
if (onlyRightButton.isSelected()) {
nodes.add(node);
}
}
}
rootNode = new UINode(getTreeTableModel().getColumnCount(), "<root>", false);
hierarchy = hierarchyComboBox.getSelectedItem();
// Build the hierarchy:
if (hierarchy == FolderSettings.FolderView.packageView) {
for (JMDiffNode node : nodes) {
parent = node.getParent();
uiNode = new UINode(getTreeTableModel().getColumnCount(), node);
if (parent != null) {
String parentName = parent.getName();
uiParentNode = new UINode(getTreeTableModel().getColumnCount(), parent);
uiParentNode = rootNode.addChild(uiParentNode);
uiParentNode.addChild(uiNode);
} else {
rootNode.addChild(uiNode);
}
}
} else if (hierarchy == FolderSettings.FolderView.fileView) {
for (JMDiffNode node : nodes) {
rootNode.addChild(new UINode(getTreeTableModel().getColumnCount(), node));
}
} else if (hierarchy == FolderSettings.FolderView.directoryView) {
for (JMDiffNode node : nodes) {
addDirectoryViewNode(rootNode, node);
}
}
return rootNode;
}
private void addDirectoryViewNode(UINode rootNode, JMDiffNode node) {
UINode parent;
JMDiffNode uiNode;
List<JMDiffNode> uiNodes;
uiNodes = new ArrayList<JMDiffNode>();
do {
uiNodes.add(node);
}
while ((node = node.getParent()) != null);
Collections.reverse(uiNodes);
parent = rootNode;
for (int i = 1; i < uiNodes.size(); i++) {
uiNode = uiNodes.get(i);
parent = parent.addChild(new UINode(getTreeTableModel().getColumnCount(), uiNode));
}
}
protected void openFileOnRow(int row, boolean background, boolean openInNewTab) {
TreePath path = folderTreeTable.getPathForRow(row);
if (path == null) {
return;
}
UINode uiNode = (UINode) path.getLastPathComponent();
if (uiNode == null) {
return;
}
List<JMDiffNode> diffNodeList = new CollectDiffNodeLeaf(uiNode).getResult();
if (diffNodeList.isEmpty()) {
return;
}
openDiffNodeList(row, diffNodeList, background, openInNewTab);
}
/**
* Open a list of diffs
* @param row
* @param diffNodeList
* @param openInNewTab open on same tab or new tab
* @param background don't focus new tab
*/
private void openDiffNodeList(int row, List<JMDiffNode> diffNodeList, boolean background, boolean openInNewTab) {
if (openInNewTab) {
int openCount = 0;
for (JMDiffNode diffNode : diffNodeList) {
if (openCount++ > 20) {
break;
}
FileComparison fileComparison = new FileComparison(mainPanel, diffNode);
fileComparison.setOpenInBackground(background);
fileComparison.execute();
}
// Hack to make it possible to select with the MIDDLE
// button of a mouse.
if (folderTreeTable.getSelectedRow() != row) {
folderTreeTable.setRowSelectionInterval(row, row);
}
// Make sure that UP and DOWN keys work the way I want.
folderTreeTable.requestFocus();
} else {
try {
openInContext(diffNodeList.get(0));
} catch (JMeldException e) {
e.printStackTrace();
}
}
}
public void doSelectPreviousRow(ActionEvent ae) {
int row;
row = folderTreeTable.getSelectedRow() - 1;
row = row < 0 ? (folderTreeTable.getRowCount() - 1) : row;
folderTreeTable.setRowSelectionInterval(row, row);
folderTreeTable.scrollRowToVisible(row);
}
public void doSelectNextRow(ActionEvent ae) {
int row;
row = folderTreeTable.getSelectedRow() + 1;
row = row >= folderTreeTable.getRowCount() ? 0 : row;
folderTreeTable.setRowSelectionInterval(row, row);
folderTreeTable.scrollRowToVisible(row);
}
public void doNextNode(ActionEvent ae) {
int row;
row = folderTreeTable.getSelectedRow();
if (row == -1) {
return;
}
if (folderTreeTable.isCollapsed(row)) {
folderTreeTable.expandRow(row);
}
doSelectNextRow(ae);
}
public void doPreviousNode(ActionEvent ae) {
int row;
row = folderTreeTable.getSelectedRow();
if (row == -1) {
return;
}
if (folderTreeTable.isExpanded(row)) {
folderTreeTable.collapseRow(row);
}
doSelectPreviousRow(ae);
}
public void doOpenFileComparisonBackground(ActionEvent ae) {
doOpenFileComparison(ae, true);
}
public void doOpenFileComparison(ActionEvent ae) {
doOpenFileComparison(ae, false);
}
private void doOpenFileComparison(ActionEvent ae, boolean background) {
for (UINode uiNode : getSelectedUINodes()) {
FileComparison fileComparison = new FileComparison(mainPanel, uiNode.getDiffNode());
fileComparison.setOpenInBackground(background);
fileComparison.execute();
}
}
@Override
public boolean checkExit() {
return false;
}
public void doExpandAll(ActionEvent ae) {
folderTreeTable.expandAll();
}
public void doCollapseAll(ActionEvent ae) {
folderTreeTable.collapseAll();
}
public boolean isCopyToLeftEnabled() {
return !getEditorSettings().getLeftsideReadonly();
}
public void doCopyToLeft(ActionEvent ae) {
CompoundCommand cc;
cc = new CompoundCommand();
for (UINode uiNode : getSelectedUINodes()) {
try {
cc.add(uiNode, uiNode.getDiffNode().getCopyToLeftCmd());
} catch (Exception ex) {
ex.printStackTrace();
}
}
cc.execute();
}
public boolean isCopyToRightEnabled() {
return !getEditorSettings().getRightsideReadonly();
}
public void doCopyToRight(ActionEvent ae) {
CompoundCommand cc;
cc = new CompoundCommand();
for (UINode uiNode : getSelectedUINodes()) {
try {
cc.add(uiNode, uiNode.getDiffNode().getCopyToRightCmd());
} catch (Exception ex) {
ex.printStackTrace();
}
}
cc.execute();
}
class CompoundCommand
extends CompoundEdit {
List<AbstractCmd> cmds;
Map<AbstractCmd, UINode> uiNodeMap;
CompoundCommand() {
uiNodeMap = new HashMap<AbstractCmd, UINode>();
cmds = new ArrayList<AbstractCmd>();
}
void add(UINode uiNode, AbstractCmd cmd) {
if (cmd == null) {
return;
}
uiNodeMap.put(cmd, uiNode);
cmds.add(cmd);
}
void execute() {
try {
for (AbstractCmd cmd : cmds) {
cmd.execute();
addEdit(cmd);
}
end();
getUndoHandler().add(this);
compareContents();
} catch (Exception ex) {
ex.printStackTrace();
}
check();
}
@Override
public void redo() {
super.redo();
compareContents();
check();
}
@Override
public void undo() {
super.undo();
compareContents();
check();
}
private void check() {
mainPanel.checkActions();
repaint();
}
private void compareContents() {
for (AbstractCmd cmd : cmds) {
uiNodeMap.get(cmd).getDiffNode().compareContents();
}
}
}
public void doRefresh(ActionEvent ae) {
new RefreshAction().execute();
}
class RefreshAction
extends SwingWorker<String, Object> {
RefreshAction() {
}
@Override
public String doInBackground() {
diff.refresh();
return null;
}
@Override
protected void done() {
treeTableModel = null;
folderTreeTable.setTreeTableModel(getTreeTableModel());
folderTreeTable.expandAll();
}
}
public boolean isRemoveRightEnabled() {
return !getEditorSettings().getRightsideReadonly();
}
public void doRemoveRight(ActionEvent ae) {
CompoundCommand cc;
cc = new CompoundCommand();
for (UINode uiNode : getSelectedUINodes()) {
try {
cc.add(uiNode, uiNode.getDiffNode().getRemoveRightCmd());
} catch (Exception ex) {
ex.printStackTrace();
}
}
cc.execute();
}
public boolean isRemoveLeftEnabled() {
return !getEditorSettings().getLeftsideReadonly();
}
public void doRemoveLeft(ActionEvent ae) {
CompoundCommand cc;
cc = new CompoundCommand();
for (UINode uiNode : getSelectedUINodes()) {
try {
cc.add(uiNode, uiNode.getDiffNode().getRemoveLeftCmd());
} catch (Exception ex) {
ex.printStackTrace();
}
}
cc.execute();
}
public void doFilter(ActionEvent ae) {
((JMTreeTableModel) folderTreeTable.getTreeTableModel()).setRoot(getRootNode());
folderTreeTable.expandAll();
}
private EditorSettings getEditorSettings() {
return JMeldSettings.getInstance().getEditor();
}
private FolderSettings getFolderSettings() {
return JMeldSettings.getInstance().getFolder();
}
private Set<UINode> getSelectedUINodes() {
Set<UINode> result;
TreePath path;
UINode uiNode;
result = new HashSet<UINode>();
for (int row : folderTreeTable.getSelectedRows()) {
path = folderTreeTable.getPathForRow(row);
if (path == null) {
continue;
}
uiNode = (UINode) path.getLastPathComponent();
if (uiNode == null) {
continue;
}
buildResult(result, uiNode);
}
return result;
}
private void buildResult(Set<UINode> result, UINode uiNode) {
if (uiNode.isLeaf() && uiNode.getDiffNode() != null) {
result.add(uiNode);
return;
}
for (UINode node : uiNode.getChildren()) {
buildResult(result, node);
}
}
protected void openInContext(JMDiffNode diffNode) throws JMeldException { }
protected JMTreeTableModel getTreeTableModel() {
if (treeTableModel == null) {
treeTableModel = createTreeTableModel();
treeTableModel.setRoot(getRootNode());
}
return treeTableModel;
}
protected JMTreeTableModel createTreeTableModel() {
return new FolderDiffTreeTableModel();
}
public void configurationChanged() {
actionHandler.checkActions();
}
}