/*******************************************************************************
* Copyright (c) 2012, 2013, 2014, 2015 Original authors and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Original authors and others - initial API and implementation
* Roman Flueckiger <roman.flueckiger@mac.com> - Bug 451486
* Roman Flueckiger <rflueckiger@inventage.com> - Bug 459582
* Dirk Fauth <dirk.fauth@googlemail.com> - Bug 460052
******************************************************************************/
package org.eclipse.nebula.widgets.nattable.columnChooser.gui;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.nebula.widgets.nattable.columnChooser.ColumnChooserUtils;
import org.eclipse.nebula.widgets.nattable.columnChooser.ColumnEntry;
import org.eclipse.nebula.widgets.nattable.columnChooser.ColumnGroupEntry;
import org.eclipse.nebula.widgets.nattable.columnChooser.ISelectionTreeListener;
import org.eclipse.nebula.widgets.nattable.coordinate.PositionUtil;
import org.eclipse.nebula.widgets.nattable.group.ColumnGroupModel;
import org.eclipse.nebula.widgets.nattable.group.ColumnGroupModel.ColumnGroup;
import org.eclipse.nebula.widgets.nattable.group.ColumnGroupUtils;
import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer.MoveDirectionEnum;
import org.eclipse.nebula.widgets.nattable.util.ArrayUtil;
import org.eclipse.nebula.widgets.nattable.util.GUIHelper;
import org.eclipse.nebula.widgets.nattable.util.ObjectUtils;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.events.TreeEvent;
import org.eclipse.swt.events.TreeListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
public class ColumnChooserDialog extends AbstractColumnChooserDialog {
private Tree availableTree;
private Tree selectedTree;
private final String selectedLabel;
private final String availableLabel;
private ColumnGroupModel columnGroupModel;
private boolean preventHidingAllColumns = false;
public ColumnChooserDialog(Shell parentShell, String availableLabel, String selectedLabel) {
super(parentShell);
this.availableLabel = availableLabel;
this.selectedLabel = selectedLabel;
}
@Override
public void populateDialogArea(Composite parent) {
GridDataFactory.fillDefaults().grab(true, true).applyTo(parent);
parent.setLayout(new GridLayout(4, false));
createLabels(parent, this.availableLabel, this.selectedLabel);
this.availableTree = new Tree(parent, SWT.MULTI | SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL | SWT.Expand);
GridData gridData = GridDataFactory.fillDefaults().grab(true, true).create();
this.availableTree.setLayoutData(gridData);
this.availableTree.addMouseListener(new MouseAdapter() {
@Override
public void mouseDoubleClick(MouseEvent e) {
addSelected();
}
});
this.availableTree.addKeyListener(new KeyAdapter() {
@Override
public void keyReleased(KeyEvent e) {
if (e.character == ' ')
addSelected();
}
});
Composite buttonComposite = new Composite(parent, SWT.NONE);
buttonComposite.setLayout(new GridLayout(1, true));
Button addButton = new Button(buttonComposite, SWT.PUSH);
addButton.setImage(GUIHelper.getImage("arrow_right")); //$NON-NLS-1$
gridData = GridDataFactory.fillDefaults().grab(false, true).align(SWT.CENTER, SWT.CENTER).create();
addButton.setLayoutData(gridData);
addButton.addSelectionListener(new SelectionListener() {
@Override
public void widgetDefaultSelected(SelectionEvent e) {
widgetSelected(e);
}
@Override
public void widgetSelected(SelectionEvent e) {
addSelected();
}
});
final Button removeButton = new Button(buttonComposite, SWT.PUSH);
removeButton.setImage(GUIHelper.getImage("arrow_left")); //$NON-NLS-1$
gridData = GridDataFactory.copyData(gridData);
removeButton.setLayoutData(gridData);
removeButton.addSelectionListener(new SelectionListener() {
@Override
public void widgetDefaultSelected(SelectionEvent e) {
widgetSelected(e);
}
@Override
public void widgetSelected(SelectionEvent e) {
removeSelected();
}
});
this.selectedTree = new Tree(parent, SWT.MULTI | SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL | SWT.Expand);
gridData = GridDataFactory.fillDefaults().grab(true, true).create();
this.selectedTree.setLayoutData(gridData);
this.selectedTree.addMouseListener(new MouseAdapter() {
@Override
public void mouseDoubleClick(MouseEvent e) {
if (ColumnChooserDialog.this.preventHidingAllColumns) {
if (!isSelectedTreeCompletelySelected()) {
removeSelected();
}
} else {
removeSelected();
}
}
});
this.selectedTree.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
boolean controlMask = (e.stateMask & SWT.MOD1) == SWT.MOD1;
if (controlMask && e.keyCode == SWT.ARROW_UP) {
moveSelectedUp();
e.doit = false;
} else if (controlMask && e.keyCode == SWT.ARROW_DOWN) {
moveSelectedDown();
e.doit = false;
}
}
@Override
public void keyReleased(KeyEvent e) {
if (e.character == ' ')
removeSelected();
}
});
this.selectedTree.addTreeListener(new TreeListener() {
@Override
public void treeCollapsed(TreeEvent event) {
selectedTreeCollapsed(event);
}
@Override
public void treeExpanded(TreeEvent event) {
selectedTreeExpanded(event);
}
});
this.selectedTree.addSelectionListener(new SelectionListener() {
@Override
public void widgetDefaultSelected(SelectionEvent event) {
widgetSelected(event);
}
@Override
public void widgetSelected(SelectionEvent event) {
toggleColumnGroupSelection((TreeItem) event.item);
if (ColumnChooserDialog.this.preventHidingAllColumns) {
removeButton.setEnabled(!isSelectedTreeCompletelySelected());
}
}
});
Composite upDownbuttonComposite = new Composite(parent, SWT.NONE);
upDownbuttonComposite.setLayout(new GridLayout(1, true));
Button topButton = new Button(upDownbuttonComposite, SWT.PUSH);
topButton.setImage(GUIHelper.getImage("arrow_up_top")); //$NON-NLS-1$
gridData = GridDataFactory.fillDefaults().grab(false, true).align(SWT.CENTER, SWT.CENTER).create();
topButton.setLayoutData(gridData);
topButton.addSelectionListener(new SelectionListener() {
@Override
public void widgetDefaultSelected(SelectionEvent e) {
widgetSelected(e);
}
@Override
public void widgetSelected(SelectionEvent e) {
moveSelectedToTop();
}
});
Button upButton = new Button(upDownbuttonComposite, SWT.PUSH);
upButton.setImage(GUIHelper.getImage("arrow_up")); //$NON-NLS-1$
gridData = GridDataFactory.fillDefaults().grab(false, true).align(SWT.CENTER, SWT.CENTER).create();
upButton.setLayoutData(gridData);
upButton.addSelectionListener(new SelectionListener() {
@Override
public void widgetDefaultSelected(SelectionEvent e) {
widgetSelected(e);
}
@Override
public void widgetSelected(SelectionEvent e) {
moveSelectedUp();
}
});
Button downButton = new Button(upDownbuttonComposite, SWT.PUSH);
downButton.setImage(GUIHelper.getImage("arrow_down")); //$NON-NLS-1$
gridData = GridDataFactory.copyData(gridData);
downButton.setLayoutData(gridData);
downButton.addSelectionListener(new SelectionListener() {
@Override
public void widgetDefaultSelected(SelectionEvent e) {
widgetSelected(e);
}
@Override
public void widgetSelected(SelectionEvent e) {
moveSelectedDown();
}
});
Button bottomButton = new Button(upDownbuttonComposite, SWT.PUSH);
bottomButton.setImage(GUIHelper.getImage("arrow_down_end")); //$NON-NLS-1$
gridData = GridDataFactory.copyData(gridData);
bottomButton.setLayoutData(gridData);
bottomButton.addSelectionListener(new SelectionListener() {
@Override
public void widgetDefaultSelected(SelectionEvent e) {
widgetSelected(e);
}
@Override
public void widgetSelected(SelectionEvent e) {
moveSelectedToBottom();
}
});
}
protected final void fireItemsSelected(List<ColumnEntry> addedItems) {
for (Object listener : this.listeners.getListeners()) {
((ISelectionTreeListener) listener).itemsSelected(addedItems);
}
}
protected final void fireItemsRemoved(List<ColumnEntry> removedItems) {
for (Object listener : this.listeners.getListeners()) {
((ISelectionTreeListener) listener).itemsRemoved(removedItems);
}
}
protected final void fireItemsMoved(MoveDirectionEnum direction,
List<ColumnGroupEntry> selectedColumnGroupEntries,
List<ColumnEntry> selectedColumnEntries,
List<List<Integer>> fromPositions, List<Integer> toPositions) {
for (Object listener : this.listeners.getListeners()) {
((ISelectionTreeListener) listener).itemsMoved(direction, selectedColumnGroupEntries, selectedColumnEntries, fromPositions, toPositions);
}
}
private void fireGroupExpanded(ColumnGroupEntry columnGroupEntry) {
for (Object listener : this.listeners.getListeners()) {
((ISelectionTreeListener) listener).itemsExpanded(columnGroupEntry);
}
}
private void fireGroupCollapsed(ColumnGroupEntry columnGroupEntry) {
for (Object listener : this.listeners.getListeners()) {
((ISelectionTreeListener) listener).itemsCollapsed(columnGroupEntry);
}
}
public void populateSelectedTree(List<ColumnEntry> columnEntries, ColumnGroupModel columnGroupModel) {
populateModel(this.selectedTree, columnEntries, columnGroupModel);
}
public void populateAvailableTree(List<ColumnEntry> columnEntries, ColumnGroupModel columnGroupModel) {
populateModel(this.availableTree, columnEntries, columnGroupModel);
}
/**
* Populates the tree. Looks for column group and adds an extra node for the
* group. The column leaves carry a {@link ColumnEntry} object as data. The
* column group leaves carry a {@link ColumnGroupEntry} object as data.
*/
private void populateModel(Tree tree, List<ColumnEntry> columnEntries, ColumnGroupModel columnGroupModel) {
this.columnGroupModel = columnGroupModel;
for (ColumnEntry columnEntry : columnEntries) {
TreeItem treeItem;
int columnEntryIndex = columnEntry.getIndex().intValue();
// Create a node for the column group - if needed
if (columnGroupModel != null
&& columnGroupModel.isPartOfAGroup(columnEntryIndex)) {
ColumnGroup columnGroup = columnGroupModel.getColumnGroupByIndex(columnEntryIndex);
String columnGroupName = columnGroup.getName();
TreeItem columnGroupTreeItem = getTreeItem(tree, columnGroupName);
if (columnGroupTreeItem == null) {
columnGroupTreeItem = new TreeItem(tree, SWT.NONE);
ColumnGroupEntry columnGroupEntry = new ColumnGroupEntry(columnGroupName, columnEntry.getPosition(), columnEntry.getIndex(), columnGroup.isCollapsed());
columnGroupTreeItem.setData(columnGroupEntry);
columnGroupTreeItem.setText(columnGroupEntry.getLabel());
}
treeItem = new TreeItem(columnGroupTreeItem, SWT.NONE);
} else {
treeItem = new TreeItem(tree, SWT.NONE);
}
treeItem.setText(columnEntry.getLabel());
treeItem.setData(columnEntry);
}
}
/**
* If the tree contains an item with the given label return it
*/
private TreeItem getTreeItem(Tree tree, String label) {
for (TreeItem treeItem : tree.getItems()) {
if (treeItem.getText().equals(label)) {
return treeItem;
}
}
return null;
}
/**
* Get the ColumnEntries from the selected TreeItem(s) Includes nested
* column group entries - if the column group is selected. Does not include
* parent of the nested entries - since that does not denote an actual
* column
*/
private List<ColumnEntry> getColumnEntriesIncludingNested(TreeItem[] selectedTreeItems) {
List<ColumnEntry> selectedColumnEntries = new ArrayList<ColumnEntry>();
for (int i = 0; i < selectedTreeItems.length; i++) {
// Column Group selected - get all children
if (isColumnGroupLeaf(selectedTreeItems[i])) {
TreeItem[] itemsInGroup = selectedTreeItems[i].getItems();
for (TreeItem itemInGroup : itemsInGroup) {
selectedColumnEntries.add((ColumnEntry) itemInGroup.getData());
}
} else {
// Column
selectedColumnEntries.add(getColumnEntryInLeaf(selectedTreeItems[i]));
}
}
return selectedColumnEntries;
}
private List<ColumnGroupEntry> getSelectedColumnGroupEntries(TreeItem[] selectedTreeItems) {
List<ColumnGroupEntry> selectedColumnGroups = new ArrayList<ColumnGroupEntry>();
for (int i = 0; i < selectedTreeItems.length; i++) {
if (isColumnGroupLeaf(selectedTreeItems[i])) {
selectedColumnGroups.add((ColumnGroupEntry) selectedTreeItems[i].getData());
}
}
return selectedColumnGroups;
}
private List<ColumnEntry> getSelectedColumnEntriesIncludingNested(Tree tree) {
return getColumnEntriesIncludingNested(tree.getSelection());
}
private List<ColumnGroupEntry> getSelectedColumnGroupEntries(Tree tree) {
return getSelectedColumnGroupEntries(tree.getSelection());
}
// Event handlers
/**
* Add selected items: 'Available tree' --> 'Selected tree' Notify
* listeners.
*/
private void addSelected() {
if (isAnyLeafSelected(this.availableTree)) {
TreeItem topAvailableItem = this.availableTree.getTopItem();
int topAvailableIndex = this.availableTree.indexOf(topAvailableItem);
TreeItem topSelectedItem = this.selectedTree.getTopItem();
int topSelectedIndex = topSelectedItem != null ? this.selectedTree.indexOf(topSelectedItem) : 0;
fireItemsSelected(getSelectedColumnEntriesIncludingNested(this.availableTree));
if (topAvailableIndex > -1 && topAvailableIndex < this.availableTree.getItemCount()) {
this.availableTree.setTopItem(this.availableTree.getItem(topAvailableIndex));
}
if (topSelectedIndex > -1 && topSelectedIndex < this.selectedTree.getItemCount()) {
this.selectedTree.setTopItem(this.selectedTree.getItem(topSelectedIndex));
}
}
}
/**
* Add selected items: 'Available tree' <-- 'Selected tree' Notify
* listeners.
*/
private void removeSelected() {
if (isAnyLeafSelected(this.selectedTree)) {
TreeItem topAvailableItem = this.availableTree.getTopItem();
int topIndex = topAvailableItem == null ? -1 : this.availableTree.indexOf(topAvailableItem);
TreeItem topSelectedItem = this.selectedTree.getTopItem();
int topSelectedIndex = topSelectedItem == null ? -1 : this.selectedTree.indexOf(topSelectedItem);
fireItemsRemoved(getSelectedColumnEntriesIncludingNested(this.selectedTree));
if (topIndex > -1 && topIndex < this.availableTree.getItemCount()) {
this.availableTree.setTopItem(this.availableTree.getItem(topIndex));
}
if (topSelectedIndex > -1 && topSelectedIndex < this.selectedTree.getItemCount()) {
this.selectedTree.setTopItem(this.selectedTree.getItem(topSelectedIndex));
}
}
}
private void selectedTreeCollapsed(TreeEvent event) {
TreeItem item = (TreeItem) event.item;
ColumnGroupEntry columnGroupEntry = (ColumnGroupEntry) item.getData();
fireGroupCollapsed(columnGroupEntry);
}
private void selectedTreeExpanded(TreeEvent event) {
TreeItem item = (TreeItem) event.item;
ColumnGroupEntry columnGroupEntry = (ColumnGroupEntry) item.getData();
fireGroupExpanded(columnGroupEntry);
}
private void toggleColumnGroupSelection(TreeItem treeItem) {
if (isColumnGroupLeaf(treeItem)) {
Collection<TreeItem> selectedLeaves = ArrayUtil.asCollection(this.selectedTree.getSelection());
boolean selected = selectedLeaves.contains(treeItem);
if (selected) {
selectAllChildren(this.selectedTree, treeItem);
} else {
unSelectAllChildren(this.selectedTree, treeItem);
}
}
}
private void selectAllChildren(Tree tree, TreeItem treeItem) {
Collection<TreeItem> selectedLeaves = ArrayUtil.asCollection(tree.getSelection());
if (isColumnGroupLeaf(treeItem)) {
selectedLeaves.addAll(ArrayUtil.asCollection(treeItem.getItems()));
}
tree.setSelection(selectedLeaves.toArray(new TreeItem[] {}));
tree.showSelection();
}
private void unSelectAllChildren(Tree tree, TreeItem treeItem) {
Collection<TreeItem> selectedLeaves = ArrayUtil.asCollection(tree.getSelection());
if (isColumnGroupLeaf(treeItem)) {
selectedLeaves.removeAll(ArrayUtil.asCollection(treeItem.getItems()));
}
tree.setSelection(selectedLeaves.toArray(new TreeItem[] {}));
tree.showSelection();
}
void moveSelectedToTop() {
if (isAnyLeafSelected(this.selectedTree)) {
if (!isFirstLeafSelected(this.selectedTree)) {
List<ColumnEntry> selectedColumnEntries = getSelectedColumnEntriesIncludingNested(this.selectedTree);
List<ColumnGroupEntry> selectedColumnGroupEntries = getSelectedColumnGroupEntries(this.selectedTree);
List<Integer> allSelectedPositions = merge(selectedColumnEntries, selectedColumnGroupEntries);
// Group continuous positions
List<List<Integer>> postionsGroupedByContiguous = PositionUtil.getGroupedByContiguous(allSelectedPositions);
List<Integer> toPositions = new ArrayList<Integer>();
int shift = getUpperMostPosition();
for (List<Integer> groupedPositions : postionsGroupedByContiguous) {
// check for unbreakable group
// Position of first element in list
int firstPositionInGroup = groupedPositions.get(0);
// Column entry
ColumnEntry columnEntry = getNextColumnEntryForPosition(this.selectedTree, firstPositionInGroup);
int columnEntryIndex = columnEntry.getIndex();
if (this.columnGroupModel != null && this.columnGroupModel.isPartOfAnUnbreakableGroup(columnEntryIndex)) {
List<Integer> groupMembers = this.columnGroupModel.getColumnGroupByIndex(columnEntryIndex).getMembers();
int groupUpperMost = groupMembers.get(0);
toPositions.add(groupUpperMost);
}
else {
toPositions.add(shift);
shift += groupedPositions.size();
}
}
fireItemsMoved(MoveDirectionEnum.UP, selectedColumnGroupEntries, selectedColumnEntries, postionsGroupedByContiguous, toPositions);
}
}
}
/**
* Move columns <i>up</i> in the 'Selected' Tree (Right)
*/
@SuppressWarnings("boxing")
protected void moveSelectedUp() {
if (isAnyLeafSelected(this.selectedTree)) {
if (!isFirstLeafSelected(this.selectedTree)) {
List<ColumnEntry> selectedColumnEntries = getSelectedColumnEntriesIncludingNested(this.selectedTree);
List<ColumnGroupEntry> selectedColumnGroupEntries = getSelectedColumnGroupEntries(this.selectedTree);
List<Integer> allSelectedPositions = merge(selectedColumnEntries, selectedColumnGroupEntries);
// Group continuous positions. If a column group moves, a bunch
// of 'from' positions move to a single 'to' position
List<List<Integer>> postionsGroupedByContiguous = PositionUtil.getGroupedByContiguous(allSelectedPositions);
List<Integer> toPositions = new ArrayList<Integer>();
// Set destination positions
for (List<Integer> groupedPositions : postionsGroupedByContiguous) {
// Do these contiguous positions contain a column group ?
boolean columnGroupMoved = columnGroupMoved(groupedPositions, selectedColumnGroupEntries);
// If already at first position do not move
int firstPositionInGroup = groupedPositions.get(0);
// Column entry
ColumnEntry columnEntry = getPreviousColumnEntryForPosition(this.selectedTree, firstPositionInGroup);
int columnEntryIndex = columnEntry.getIndex();
// Previous column entry
ColumnEntry previousColumnEntry = getPreviousColumnEntryForPosition(this.selectedTree, firstPositionInGroup - 1);
// Previous column entry is null if the last leaf in the
// tree is selected
if (previousColumnEntry == null) {
toPositions.add(firstPositionInGroup);
}
else {
int previousColumnEntryIndex = previousColumnEntry.getIndex();
if (columnGroupMoved) {
// If the previous entry is a column group
// move above it.
if (this.columnGroupModel != null && this.columnGroupModel.isPartOfAGroup(previousColumnEntryIndex)) {
ColumnGroup previousColumnGroup = this.columnGroupModel.getColumnGroupByIndex(previousColumnEntryIndex);
toPositions.add(firstPositionInGroup - previousColumnGroup.getSize());
} else {
toPositions.add(firstPositionInGroup - 1);
}
} else {
// If is first member of the unbreakable group,
// can't move up i.e. out of the group
if (this.columnGroupModel != null && this.columnGroupModel.isPartOfAnUnbreakableGroup(columnEntryIndex)
&& !ColumnGroupUtils.isInTheSameGroup(columnEntryIndex, previousColumnEntryIndex, this.columnGroupModel)) {
return;
}
// If previous entry is an unbreakable column group
// move above it
if (this.columnGroupModel != null && this.columnGroupModel.isPartOfAnUnbreakableGroup(previousColumnEntryIndex)
&& !this.columnGroupModel.isPartOfAGroup(columnEntryIndex)
&& !ColumnGroupUtils.isInTheSameGroup(columnEntryIndex, previousColumnEntryIndex, this.columnGroupModel)) {
ColumnGroup previousColumnGroup = this.columnGroupModel.getColumnGroupByIndex(previousColumnEntryIndex);
toPositions.add(firstPositionInGroup - previousColumnGroup.getSize());
} else {
toPositions.add(firstPositionInGroup - 1);
}
}
}
}
fireItemsMoved(MoveDirectionEnum.UP, selectedColumnGroupEntries, selectedColumnEntries, postionsGroupedByContiguous, toPositions);
}
}
}
private List<Integer> merge(List<ColumnEntry> selectedColumnEntries, List<ColumnGroupEntry> selectedColumnGroupEntries) {
// Convert to positions
List<Integer> columnEntryPositions = ColumnChooserUtils.getColumnEntryPositions(selectedColumnEntries);
List<Integer> columnGroupEntryPositions = ColumnGroupEntry.getColumnGroupEntryPositions(selectedColumnGroupEntries);
// Selected columns + column groups
Set<Integer> allSelectedPositionsSet = new HashSet<Integer>();
allSelectedPositionsSet.addAll(columnEntryPositions);
allSelectedPositionsSet.addAll(columnGroupEntryPositions);
List<Integer> allSelectedPositions = new ArrayList<Integer>(allSelectedPositionsSet);
Collections.sort(allSelectedPositions);
return allSelectedPositions;
}
/**
* Move columns <i>down</i> in the 'Selected' Tree (Right)
*/
@SuppressWarnings("boxing")
protected void moveSelectedDown() {
if (isAnyLeafSelected(this.selectedTree)) {
if (!isLastLeafSelected(this.selectedTree)) {
List<ColumnEntry> selectedColumnEntries = getSelectedColumnEntriesIncludingNested(this.selectedTree);
List<ColumnGroupEntry> selectedColumnGroupEntries = getSelectedColumnGroupEntries(this.selectedTree);
List<Integer> allSelectedPositions = merge(selectedColumnEntries, selectedColumnGroupEntries);
// Group continuous positions
List<List<Integer>> postionsGroupedByContiguous = PositionUtil.getGroupedByContiguous(allSelectedPositions);
List<Integer> toPositions = new ArrayList<Integer>();
// Set destination positions
for (List<Integer> groupedPositions : postionsGroupedByContiguous) {
// Do these contiguous positions contain a column group ?
boolean columnGroupMoved = columnGroupMoved(groupedPositions, selectedColumnGroupEntries);
// Position of last element in list
int lastListIndex = groupedPositions.size() - 1;
int lastPositionInGroup = groupedPositions.get(lastListIndex);
// Column entry
ColumnEntry columnEntry = getNextColumnEntryForPosition(this.selectedTree, lastPositionInGroup);
int columnEntryIndex = columnEntry.getIndex();
// Next Column Entry
ColumnEntry nextColumnEntry = getNextColumnEntryForPosition(this.selectedTree, lastPositionInGroup + 1);
// Next column entry will be null the last leaf in the tree
// is selected
if (nextColumnEntry == null) {
toPositions.add(lastPositionInGroup);
}
else {
int nextColumnEntryIndex = nextColumnEntry.getIndex();
if (columnGroupMoved) {
// If the next entry is a column group
// move past it.
if (this.columnGroupModel != null && this.columnGroupModel.isPartOfAGroup(nextColumnEntryIndex)) {
ColumnGroup nextColumnGroup = this.columnGroupModel.getColumnGroupByIndex(nextColumnEntryIndex);
toPositions.add(lastPositionInGroup + nextColumnGroup.getSize());
} else {
toPositions.add(lastPositionInGroup + 1);
}
} else {
// If is last member of the unbreakable group, can't
// move down i.e. out of the group
if (this.columnGroupModel != null && this.columnGroupModel.isPartOfAnUnbreakableGroup(columnEntryIndex)
&& !ColumnGroupUtils.isInTheSameGroup(columnEntryIndex, nextColumnEntryIndex, this.columnGroupModel)) {
return;
}
// If next entry is an unbreakable column group
// move past it
if (this.columnGroupModel != null && this.columnGroupModel.isPartOfAnUnbreakableGroup(nextColumnEntryIndex)
&& !this.columnGroupModel.isPartOfAGroup(columnEntryIndex)
&& !ColumnGroupUtils.isInTheSameGroup(columnEntryIndex, nextColumnEntryIndex, this.columnGroupModel)) {
ColumnGroup nextColumnGroup = this.columnGroupModel.getColumnGroupByIndex(nextColumnEntryIndex);
toPositions.add(lastPositionInGroup + nextColumnGroup.getSize());
}
else {
toPositions.add(lastPositionInGroup + 1);
}
}
}
}
fireItemsMoved(MoveDirectionEnum.DOWN, selectedColumnGroupEntries, selectedColumnEntries, postionsGroupedByContiguous, toPositions);
}
}
}
void moveSelectedToBottom() {
if (isAnyLeafSelected(this.selectedTree)) {
if (!isLastLeafSelected(this.selectedTree)) {
List<ColumnEntry> selectedColumnEntries = getSelectedColumnEntriesIncludingNested(this.selectedTree);
List<ColumnGroupEntry> selectedColumnGroupEntries = getSelectedColumnGroupEntries(this.selectedTree);
List<Integer> allSelectedPositions = merge(selectedColumnEntries, selectedColumnGroupEntries);
// Group continuous positions
List<List<Integer>> positionsGroupedByContiguous = PositionUtil.getGroupedByContiguous(allSelectedPositions);
List<Integer> toPositions = new ArrayList<Integer>();
List<List<Integer>> reversed = new ArrayList<List<Integer>>(positionsGroupedByContiguous);
Collections.reverse(reversed);
int lowerMost = getLowerMostPosition();
int shift = 0;
for (List<Integer> groupedPositions : reversed) {
// check for unbreakable group
// Position of last element in list
int lastListIndex = groupedPositions.size() - 1;
int lastPositionInGroup = groupedPositions.get(lastListIndex);
// Column entry
ColumnEntry columnEntry = getNextColumnEntryForPosition(this.selectedTree, lastPositionInGroup);
int columnEntryIndex = columnEntry.getIndex();
if (this.columnGroupModel != null && this.columnGroupModel.isPartOfAnUnbreakableGroup(columnEntryIndex)) {
List<Integer> groupMembers = this.columnGroupModel.getColumnGroupByIndex(columnEntryIndex).getMembers();
int groupLowerMost = groupMembers.get(groupMembers.size() - 1);
toPositions.add(groupLowerMost);
}
else {
toPositions.add(Integer.valueOf(lowerMost - shift));
shift += groupedPositions.size();
}
}
fireItemsMoved(MoveDirectionEnum.DOWN, selectedColumnGroupEntries, selectedColumnEntries, reversed, toPositions);
}
}
}
private boolean columnGroupMoved(List<Integer> fromPositions, List<ColumnGroupEntry> movedColumnGroupEntries) {
for (ColumnGroupEntry columnGroupEntry : movedColumnGroupEntries) {
if (fromPositions.contains(columnGroupEntry.getFirstElementPosition()))
return true;
}
return false;
}
/**
* Get the ColumnEntry in the tree with the given position
*/
private ColumnEntry getColumnEntryForPosition(Tree tree, int columnEntryPosition) {
List<ColumnEntry> allColumnEntries = getColumnEntriesIncludingNested(this.selectedTree.getItems());
for (ColumnEntry columnEntry : allColumnEntries) {
if (columnEntry.getPosition().intValue() == columnEntryPosition) {
return columnEntry;
}
}
return null;
}
private ColumnEntry getPreviousColumnEntryForPosition(Tree tree, int columnEntryPosition) {
ColumnEntry result = null;
while (result == null && columnEntryPosition >= getUpperMostPosition()) {
result = getColumnEntryForPosition(tree, columnEntryPosition);
if (result == null) {
columnEntryPosition--;
}
}
return result;
}
private ColumnEntry getNextColumnEntryForPosition(Tree tree, int columnEntryPosition) {
ColumnEntry result = null;
while (result == null && columnEntryPosition <= getLowerMostPosition()) {
result = getColumnEntryForPosition(tree, columnEntryPosition);
if (result == null) {
columnEntryPosition++;
}
}
return result;
}
// Leaf related methods
/**
* Get Leaf index of the selected leaves in the tree
*/
protected List<Integer> getIndexesOfSelectedLeaves(Tree tree) {
List<TreeItem> allSelectedLeaves = ArrayUtil.asList(tree.getSelection());
List<Integer> allSelectedIndexes = new ArrayList<Integer>();
for (TreeItem selectedLeaf : allSelectedLeaves) {
allSelectedIndexes.add(Integer.valueOf(tree.indexOf(selectedLeaf)));
}
return allSelectedIndexes;
}
public void expandAllLeaves() {
List<TreeItem> allLeaves = ArrayUtil.asList(this.selectedTree.getItems());
for (TreeItem leaf : allLeaves) {
if (isColumnGroupLeaf(leaf)) {
ColumnGroupEntry columnGroupEntry = (ColumnGroupEntry) leaf.getData();
leaf.setExpanded(!columnGroupEntry.isCollapsed());
}
}
}
private boolean isColumnGroupLeaf(TreeItem treeItem) {
if (ObjectUtils.isNotNull(treeItem)) {
return treeItem.getData() instanceof ColumnGroupEntry;
} else {
return false;
}
}
private boolean isLastLeafSelected(Tree tree) {
TreeItem[] selectedLeaves = tree.getSelection();
for (int i = 0; i < selectedLeaves.length; i++) {
if (tree.indexOf(selectedLeaves[i]) + 1 == tree.getItemCount()) {
return true;
}
}
return false;
}
private boolean isFirstLeafSelected(Tree tree) {
TreeItem[] selectedLeaves = tree.getSelection();
for (int i = 0; i < selectedLeaves.length; i++) {
if (this.selectedTree.indexOf(selectedLeaves[i]) == 0) {
return true;
}
}
return false;
}
private boolean isAnyLeafSelected(Tree tree) {
TreeItem[] selectedLeaves = tree.getSelection();
return selectedLeaves != null && selectedLeaves.length > 0;
}
private ColumnEntry getColumnEntryInLeaf(TreeItem leaf) {
if (!isColumnGroupLeaf(leaf)) {
return (ColumnEntry) leaf.getData();
} else {
return null;
}
}
public void removeAllLeaves() {
this.selectedTree.removeAll();
this.availableTree.removeAll();
}
// Leaf Selection
public void setSelectionIncludingNested(List<Integer> indexes) {
setSelectionIncludingNested(this.selectedTree, indexes);
}
/**
* Marks the leaves in the tree as selected
*
* @param tree
* containing the leaves
* @param indexes
* index of the leaf in the tree
*/
protected void setSelection(Tree tree, List<Integer> indexes) {
List<TreeItem> selectedLeaves = new ArrayList<TreeItem>();
for (Integer leafIndex : indexes) {
selectedLeaves.add(tree.getItem(leafIndex.intValue()));
}
tree.setSelection(selectedLeaves.toArray(new TreeItem[] {}));
tree.showSelection();
}
/**
* Mark the leaves with matching column entries as selected. Also checks all
* the children of the column group leaves
*
* @param columnEntryIndexes
* index of the ColumnEntry in the leaf
*/
private void setSelectionIncludingNested(Tree tree, List<Integer> columnEntryIndexes) {
Collection<TreeItem> allLeaves = ArrayUtil.asCollection(tree.getItems());
List<TreeItem> selectedLeaves = new ArrayList<TreeItem>();
for (TreeItem leaf : allLeaves) {
if (!isColumnGroupLeaf(leaf)) {
int index = getColumnEntryInLeaf(leaf).getIndex().intValue();
if (columnEntryIndexes.contains(Integer.valueOf(index))) {
selectedLeaves.add(leaf);
}
} else {
// Check all children in column groups
Collection<TreeItem> columnGroupLeaves = ArrayUtil.asCollection(leaf.getItems());
for (TreeItem columnGroupLeaf : columnGroupLeaves) {
int index = getColumnEntryInLeaf(columnGroupLeaf).getIndex().intValue();
if (columnEntryIndexes.contains(Integer.valueOf(index))) {
selectedLeaves.add(columnGroupLeaf);
}
}
}
}
tree.setSelection(selectedLeaves.toArray(new TreeItem[] {}));
setGroupsSelectionIfRequired(tree, columnEntryIndexes);
tree.showSelection();
}
/**
* If all the leaves in a group are selected the group is also selected
*/
private void setGroupsSelectionIfRequired(Tree tree, List<Integer> columnEntryIndexes) {
Collection<TreeItem> allLeaves = ArrayUtil.asCollection(tree.getItems());
Collection<TreeItem> selectedLeaves = ArrayUtil.asCollection(tree.getSelection());
for (TreeItem leaf : allLeaves) {
if (isColumnGroupLeaf(leaf)) {
boolean markSelected = true;
Collection<TreeItem> nestedLeaves = ArrayUtil.asCollection(leaf.getItems());
for (TreeItem nestedLeaf : nestedLeaves) {
ColumnEntry columnEntry = getColumnEntryInLeaf(nestedLeaf);
if (!columnEntryIndexes.contains(columnEntry.getIndex())) {
markSelected = false;
}
}
if (markSelected) {
selectedLeaves.add(leaf);
}
}
}
tree.setSelection(selectedLeaves.toArray(new TreeItem[] {}));
}
protected Tree getSelectedTree() {
return this.selectedTree;
}
/**
* With this option, the dialog can be configure to either allow removing
* all columns from the set of visible columns or prevent such a state by
* disabling the "remove from selection" button if the selection contains
* all remaining visible columns.
*
* @param preventHidingAllColumns
* if true, the dialog will prevent that the user selects no
* column at all, by disabling the "remove from selection" button
* appropriately.
*/
public void setPreventHidingAllColumns(boolean preventHidingAllColumns) {
this.preventHidingAllColumns = preventHidingAllColumns;
}
private boolean isSelectedTreeCompletelySelected() {
final TreeItem[] selection = this.selectedTree.getSelection();
final TreeItem[] items = this.selectedTree.getItems();
for (TreeItem item : items) {
// if any one item is NOT completely selected, the tree is NOT
// completely selected.
if (!isItemCompletelySelected(item, ArrayUtil.asCollection(selection))) {
return false;
}
}
return true;
}
private boolean isItemCompletelySelected(TreeItem item, Collection<TreeItem> selectedItems) {
if (selectedItems.contains(item)) {
// sub-root is selected: need not look any further, the whole
// subtree is thereby selected
return true;
} else {
TreeItem[] children = item.getItems();
if (children.length == 0) {
return false;
}
else {
// if there are children: check if all children are selected
// (which means the sub-root is implicitly selected)
for (TreeItem child : item.getItems()) {
if (!isItemCompletelySelected(child, selectedItems)) {
return false;
}
}
return true;
}
}
}
private int getUpperMostPosition() {
List<ColumnEntry> entries = getColumnEntriesIncludingNested(this.selectedTree.getItems());
return entries.get(0).getPosition();
}
private int getLowerMostPosition() {
List<ColumnEntry> entries = getColumnEntriesIncludingNested(this.selectedTree.getItems());
return entries.get(entries.size() - 1).getPosition();
}
}