package io.sloeber.ui.preferences;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.preference.PreferencePage;
import org.eclipse.jface.viewers.CellLabelProvider;
import org.eclipse.jface.viewers.CheckboxTreeViewer;
import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
import org.eclipse.jface.viewers.ICheckStateProvider;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CCombo;
import org.eclipse.swt.custom.TreeEditor;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPreferencePage;
import org.eclipse.ui.dialogs.FilteredTree;
import org.eclipse.ui.dialogs.PatternFilter;
import io.sloeber.core.api.LibraryManager;
import io.sloeber.core.api.LibraryManager.LibraryTree;
import io.sloeber.core.api.Node;
import io.sloeber.core.api.VersionNumber;
import io.sloeber.ui.Activator;
import io.sloeber.ui.Messages;
public class LibrarySelectionPage extends PreferencePage implements IWorkbenchPreferencePage {
private FilteredTree tree;
private boolean isJobRunning = false;
protected TreeViewer viewer;
protected TreeEditor editor;
protected LibraryTree libs = LibraryManager.getLibraryTree();
final static String emptyString = ""; //$NON-NLS-1$
@Override
public void init(IWorkbench workbench) {
// nothing needed here
}
@Override
protected void performDefaults() {
this.libs.reset();
this.viewer.refresh();
if (this.editor != null && this.editor.getEditor() != null) {
this.editor.getEditor().dispose();
}
super.performDefaults();
}
@Override
protected Control createContents(Composite parent) {
Composite control = new Composite(parent, SWT.NONE);
control.setLayout(new GridLayout());
Text desc = new Text(control, SWT.READ_ONLY);
GridData layoutData = new GridData(SWT.FILL, SWT.FILL, true, false);
desc.setLayoutData(layoutData);
desc.setBackground(parent.getBackground());
desc.setText(Messages.LibraryPreferencePage_add_remove);
this.createTree(control);
return control;
}
@Override
public boolean performOk() {
if (isJobRunning == false) {
isJobRunning = true;
new Job(Messages.ui_Adopting_arduino_libraries) {
@Override
protected IStatus run(IProgressMonitor monitor) {
MultiStatus status = new MultiStatus(Activator.getId(), 0, Messages.ui_installing_arduino_libraries,
null);
return LibraryManager.setLibraryTree(LibrarySelectionPage.this.libs, monitor, status);
}
}.schedule();
return true;
} else {
return false;
}
}
public void createTree(Composite parent) {
// filtering applied to all columns
PatternFilter filter = new PatternFilter() {
@Override
protected boolean isLeafMatch(final Viewer viewer1, final Object element) {
int numberOfColumns = ((TreeViewer) viewer1).getTree().getColumnCount();
boolean isMatch = false;
for (int columnIndex = 0; columnIndex < numberOfColumns; columnIndex++) {
String labelText = LibraryLabelProvider.getColumnText(element, columnIndex);
isMatch |= wordMatches(labelText);
}
return isMatch;
}
};
this.tree = new FilteredTree(parent, SWT.CHECK | SWT.V_SCROLL | SWT.BORDER | SWT.FULL_SELECTION, filter, true) {
@SuppressWarnings("synthetic-access")
@Override
protected TreeViewer doCreateTreeViewer(Composite composite, int style) {
CheckboxTreeViewer viewer1 = new CheckboxTreeViewer(composite);
viewer1.setCheckStateProvider(new LibraryCheckProvider());
viewer1.setLabelProvider(new LibraryLabelProvider());
viewer1.setContentProvider(new LibraryContentProvider());
return viewer1;
}
};
this.viewer = this.tree.getViewer();
this.viewer.setInput(this.libs);
TreeColumn name = new TreeColumn(this.viewer.getTree(), SWT.LEFT);
name.setWidth(400);
TreeColumn version = new TreeColumn(this.viewer.getTree(), SWT.LEFT);
version.setWidth(100);
// create the editor and set its attributes
this.editor = new TreeEditor(this.viewer.getTree());
this.editor.horizontalAlignment = SWT.LEFT;
this.editor.grabHorizontal = true;
this.editor.setColumn(1);
// this ensures the tree labels are displayed correctly
this.viewer.refresh(true);
// enable tooltips
ColumnViewerToolTipSupport.enableFor(this.viewer);
// tree interactions listener
this.viewer.getTree().addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent event) {
if (LibrarySelectionPage.this.editor.getEditor() != null) {
LibrarySelectionPage.this.editor.getEditor().dispose();
}
final TreeItem item = event.item instanceof TreeItem ? (TreeItem) event.item : null;
if (item != null && event.detail == SWT.CHECK) {
if (item.getData() instanceof LibraryTree.Category) {
item.setGrayed(false);
for (LibraryTree.Library child : ((LibraryTree.Category) item.getData()).getLibraries()) {
if (item.getChecked()) {
child.setVersion(child.getLatest());
} else {
child.setVersion(null);
}
}
for (TreeItem child : item.getItems()) {
child.setChecked(item.getChecked());
if (item.getChecked()) {
child.setText(1, ((LibraryTree.Library) child.getData()).getLatest());
} else {
child.setText(1, emptyString);
}
}
} else {
if (item.getChecked()) {
((LibraryTree.Library) item.getData())
.setVersion(((LibraryTree.Library) item.getData()).getLatest());
item.setText(1, ((LibraryTree.Library) item.getData()).getLatest());
} else {
((LibraryTree.Library) item.getData()).setVersion(null);
item.setText(1, emptyString);
}
verifySubtreeCheckStatus(item.getParentItem());
}
}
if (item != null && item.getItemCount() == 0 && item.getChecked()) {
// Create the dropdown and add data to it
final CCombo combo = new CCombo(LibrarySelectionPage.this.viewer.getTree(), SWT.READ_ONLY);
for (VersionNumber version1 : ((LibraryTree.Library) item.getData()).getVersions()) {
combo.add(version1.toString());
}
// Select the previously selected item from the cell
combo.select(combo.indexOf(item.getText(1)));
// Compute the width for the editor
// Also, compute the column width, so that the dropdown fits
// Set the focus on the dropdown and set into the editor
combo.setFocus();
LibrarySelectionPage.this.editor.setEditor(combo, item, 1);
// Add a listener to set the selected item back into the
// cell
combo.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent event1) {
((LibraryTree.Library) item.getData()).setVersion(combo.getText());
item.setText(1, combo.getText());
// Item selected: end the editing session
combo.dispose();
}
});
}
}
});
}
/**
* Ensures the correct checked/unchecked/greyed attributes are set on the
* category.
*
* @param item
* the tree item representing the category
*/
protected static void verifySubtreeCheckStatus(TreeItem item) {
boolean grayed = false;
boolean checked = false;
for (TreeItem child : item.getItems()) {
if (child.getChecked()) {
checked = true;
} else {
grayed = true;
}
}
item.setChecked(checked);
item.setGrayed(grayed);
}
/**
*
* Displays the tree labels for both columns: name and version
*
*/
private static class LibraryLabelProvider extends CellLabelProvider {
@Override
public String getToolTipText(Object element) {
if (element instanceof LibraryTree.Library) {
return ((LibraryTree.Library) element).getTooltip();
}
return null;
}
public static String getColumnText(Object element, int col) {
switch (col) {
case 0:
if (element instanceof LibraryTree.Library) {
return ((LibraryTree.Library) element).getName() + " (" //$NON-NLS-1$
+ ((LibraryTree.Library) element).getIndexName() + ")"; //$NON-NLS-1$
}
return emptyString;
case 1:
if (element instanceof LibraryTree.Library) {
return ((LibraryTree.Library) element).getVersion();
}
return emptyString;
default:
break;
}
return null;
}
@Override
public Point getToolTipShift(Object object) {
return new Point(5, 5);
}
@Override
public int getToolTipDisplayDelayTime(Object object) {
return 500;
}
@Override
public int getToolTipTimeDisplayed(Object object) {
return 0;
}
@Override
public void update(ViewerCell cell) {
if (cell.getColumnIndex() == 0) {
if (cell.getElement() instanceof LibraryTree.Library) {
cell.setText(((LibraryTree.Library) cell.getElement()).getName() + " (" //$NON-NLS-1$
+ ((LibraryTree.Library) cell.getElement()).getIndexName() + ")"); //$NON-NLS-1$
} else {
cell.setText(((Node) cell.getElement()).getName());
}
} else if (cell.getElement() instanceof LibraryTree.Library) {
cell.setText(((LibraryTree.Library) cell.getElement()).getVersion());
} else {
cell.setText(null);
}
}
}
/**
* Provides the correct checked status for installed libraries
*
*/
private static class LibraryCheckProvider implements ICheckStateProvider {
@Override
public boolean isChecked(Object element) {
if (element instanceof LibraryTree.Library) {
return ((LibraryTree.Library) element).getVersion() != null;
} else if (element instanceof LibraryTree.Category) {
for (LibraryTree.Library library : ((LibraryTree.Category) element).getLibraries()) {
if (library.getVersion() != null) {
return true;
}
}
}
return false;
}
@Override
public boolean isGrayed(Object element) {
if (element instanceof LibraryTree.Category && isChecked(element)) {
for (LibraryTree.Library library : ((LibraryTree.Category) element).getLibraries()) {
if (library.getVersion() == null) {
return true;
}
}
}
return false;
}
}
/**
* Provides the tree content data
*
*/
private static class LibraryContentProvider implements ITreeContentProvider {
@Override
public Object[] getChildren(Object node) {
return ((Node) node).getChildren();
}
@Override
public Object getParent(Object node) {
if (node instanceof Node) {
return ((Node) node).getParent();
}
return null;
}
@Override
public boolean hasChildren(Object node) {
if (node instanceof LibraryTree) {
return !((LibraryTree) node).getCategories().isEmpty();
}
return ((Node) node).hasChildren();
}
@Override
public Object[] getElements(Object node) {
if (node instanceof LibraryTree) {
return ((LibraryTree) node).getCategories().toArray();
}
return getChildren(node);
}
@Override
public void dispose() {
// no code needed here
}
@Override
public void inputChanged(Viewer arg0, Object arg1, Object arg2) {
// no code needed here
}
}
}