package com.kartoflane.superluminal2.ui; import java.util.ArrayList; import java.util.Comparator; import java.util.Iterator; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.SashForm; import org.eclipse.swt.custom.ScrolledComposite; import org.eclipse.swt.events.ControlAdapter; import org.eclipse.swt.events.ControlEvent; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.MouseListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.TraverseEvent; import org.eclipse.swt.events.TraverseListener; import org.eclipse.swt.graphics.Point; 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.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Text; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeColumn; import org.eclipse.swt.widgets.TreeItem; import com.kartoflane.superluminal2.Superluminal; import com.kartoflane.superluminal2.components.Hotkey; import com.kartoflane.superluminal2.components.enums.Hotkeys; import com.kartoflane.superluminal2.components.interfaces.Predicate; import com.kartoflane.superluminal2.core.Database; import com.kartoflane.superluminal2.core.Manager; import com.kartoflane.superluminal2.ftl.AugmentObject; import com.kartoflane.superluminal2.utils.UIUtils; public class AugmentSelectionDialog { private static AugmentSelectionDialog instance = null; private static final int defaultBlueTabWidth = 200; private static final int defaultNameTabWidth = 150; private static final int minTreeWidth = defaultBlueTabWidth + defaultNameTabWidth + 5; private static final int defaultDataWidth = 200; private static final Predicate<AugmentObject> defaultFilter = new Predicate<AugmentObject>() { public boolean accept(AugmentObject object) { return true; } }; private static AugmentObject selection = null; private AugmentObject result = null; private int response = SWT.NO; private boolean sortByBlueprint = true; private Predicate<AugmentObject> filter = defaultFilter; private Shell shell = null; private Text txtDesc; private Button btnCancel; private Button btnConfirm; private Tree tree; private TreeColumn trclmnBlueprint; private TreeColumn trclmnName; private Button btnSearch; public AugmentSelectionDialog(Shell parent) { if (instance != null) throw new IllegalStateException("Previous instance has not been disposed!"); instance = this; shell = new Shell(parent, SWT.DIALOG_TRIM | SWT.RESIZE | SWT.APPLICATION_MODAL); shell.setText(Superluminal.APP_NAME + " - Augment Selection"); shell.setLayout(new GridLayout(1, false)); SashForm sashForm = new SashForm(shell, SWT.SMOOTH); sashForm.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1)); tree = new Tree(sashForm, SWT.BORDER | SWT.FULL_SELECTION); tree.setHeaderVisible(true); // remove the horizontal bar so that it doesn't flicker when the tree is resized tree.getHorizontalBar().dispose(); trclmnBlueprint = new TreeColumn(tree, SWT.LEFT); trclmnBlueprint.setWidth(defaultBlueTabWidth); trclmnBlueprint.setText("Blueprint"); trclmnName = new TreeColumn(tree, SWT.RIGHT); trclmnName.setWidth(defaultNameTabWidth); trclmnName.setText("Name"); ScrolledComposite scrolledComposite = new ScrolledComposite(sashForm, SWT.BORDER | SWT.V_SCROLL); scrolledComposite.setExpandHorizontal(true); scrolledComposite.setExpandVertical(true); Composite compData = new Composite(scrolledComposite, SWT.NONE); GridLayout gl_compData = new GridLayout(1, false); gl_compData.marginHeight = 0; gl_compData.marginWidth = 0; compData.setLayout(gl_compData); Label lblDescription = new Label(compData, SWT.NONE); lblDescription.setText("Description:"); txtDesc = new Text(compData, SWT.BORDER | SWT.READ_ONLY | SWT.WRAP | SWT.V_SCROLL | SWT.MULTI); GridData gd_txtDesc = new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1); gd_txtDesc.heightHint = 60; txtDesc.setLayoutData(gd_txtDesc); scrolledComposite.setContent(compData); scrolledComposite.setMinSize(compData.computeSize(SWT.DEFAULT, SWT.DEFAULT)); sashForm.setWeights(new int[] { minTreeWidth, defaultDataWidth }); Composite compButtons = new Composite(shell, SWT.NONE); GridLayout gl_compButtons = new GridLayout(3, false); gl_compButtons.marginWidth = 0; gl_compButtons.marginHeight = 0; compButtons.setLayout(gl_compButtons); compButtons.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 1, 1)); btnSearch = new Button(compButtons, SWT.NONE); GridData gd_btnSearch = new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1); gd_btnSearch.widthHint = 80; btnSearch.setLayoutData(gd_btnSearch); btnSearch.setText("Search"); btnConfirm = new Button(compButtons, SWT.NONE); GridData gd_btnConfirm = new GridData(SWT.RIGHT, SWT.CENTER, true, false, 1, 1); gd_btnConfirm.widthHint = 80; btnConfirm.setLayoutData(gd_btnConfirm); btnConfirm.setText("Confirm"); btnCancel = new Button(compButtons, SWT.NONE); GridData gd_btnCancel = new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1); gd_btnCancel.widthHint = 80; btnCancel.setLayoutData(gd_btnCancel); btnCancel.setText("Cancel"); tree.addMouseListener(new MouseListener() { @Override public void mouseDoubleClick(MouseEvent e) { if (e.button == 1 && tree.getSelectionCount() != 0) { TreeItem selectedItem = tree.getSelection()[0]; if (selectedItem.getItemCount() == 0 && btnConfirm.isEnabled()) btnConfirm.notifyListeners(SWT.Selection, null); else if (selectedItem.getBounds().contains(e.x, e.y)) selectedItem.setExpanded(!selectedItem.getExpanded()); } } @Override public void mouseDown(MouseEvent e) { } @Override public void mouseUp(MouseEvent e) { } }); tree.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { if (tree.getSelectionCount() != 0) { TreeItem selectedItem = tree.getSelection()[0]; Object o = selectedItem.getData(); if (o instanceof AugmentObject) { selection = (AugmentObject) o; result = selection; btnConfirm.setEnabled(result != null); } else { result = null; btnConfirm.setEnabled(false); } } else { result = null; btnConfirm.setEnabled(false); } updateData(); } }); trclmnBlueprint.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { // Sort by blueprint name if (!sortByBlueprint) { sortByBlueprint = true; updateTree(); } } }); trclmnName.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { // Sort by title if (sortByBlueprint) { sortByBlueprint = false; updateTree(); } } }); btnSearch.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { AugmentSearchDialog asDialog = new AugmentSearchDialog(shell); Predicate<AugmentObject> result = asDialog.open(); if (result == AbstractSearchDialog.RESULT_DEFAULT) { filter = defaultFilter; } else if (result == AbstractSearchDialog.RESULT_UNCHANGED) { // Do nothing } else { filter = result; } updateTree(); tree.notifyListeners(SWT.Selection, null); } }); btnCancel.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { response = SWT.NO; dispose(); } }); btnConfirm.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { response = SWT.YES; dispose(); } }); shell.addListener(SWT.Close, new Listener() { @Override public void handleEvent(Event e) { btnCancel.notifyListeners(SWT.Selection, null); e.doit = false; } }); shell.addTraverseListener(new TraverseListener() { @Override public void keyTraversed(TraverseEvent e) { if (e.detail == SWT.TRAVERSE_RETURN && tree.getSelectionCount() != 0) { TreeItem selectedItem = tree.getSelection()[0]; if (selectedItem.getItemCount() == 0 && btnConfirm.isEnabled()) btnConfirm.notifyListeners(SWT.Selection, null); else selectedItem.setExpanded(!selectedItem.getExpanded()); } } }); ControlAdapter resizer = new ControlAdapter() { @Override public void controlResized(ControlEvent e) { final int BORDER_OFFSET = tree.getBorderWidth(); if (trclmnBlueprint.getWidth() > tree.getClientArea().width - BORDER_OFFSET) trclmnBlueprint.setWidth(tree.getClientArea().width - BORDER_OFFSET); trclmnName.setWidth(tree.getClientArea().width - trclmnBlueprint.getWidth() - BORDER_OFFSET); } }; tree.addControlListener(resizer); trclmnBlueprint.addControlListener(resizer); shell.setMinimumSize(minTreeWidth + defaultDataWidth, 300); shell.pack(); Point size = shell.getSize(); shell.setSize(size.x + 5, size.y); Point parSize = parent.getSize(); Point parLoc = parent.getLocation(); shell.setLocation(parLoc.x + parSize.x / 3 - size.x / 2, parLoc.y + parSize.y / 3 - size.y / 2); // Register hotkeys Hotkey h = new Hotkey(Manager.getHotkey(Hotkeys.SEARCH)); h.addNotifyAction(btnSearch, true); Manager.hookHotkey(shell, h); } private void open() { response = SWT.NO; updateTree(); shell.open(); updateData(); Display display = UIUtils.getDisplay(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) display.sleep(); } } public AugmentObject open(AugmentObject current) { result = current; if (current == null || current == Database.DEFAULT_AUGMENT_OBJ) { result = selection; } else { selection = result; } open(); if (response == SWT.YES) return result; else return null; } private void updateTree() { for (TreeItem trtm : tree.getItems()) trtm.dispose(); TreeItem trtm = new TreeItem(tree, SWT.NONE); trtm.setText("No Augment"); trtm.setData(Database.DEFAULT_AUGMENT_OBJ); TreeItem selection = null; AugmentIterator it = new AugmentIterator(Database.getInstance().getAugments(), sortByBlueprint); for (it.first(); it.hasNext(); it.next()) { AugmentObject augment = it.current(); trtm = new TreeItem(tree, SWT.NONE); trtm.setText(0, augment.getBlueprintName()); trtm.setText(1, augment.getTitle()); trtm.setData(augment); if (result == augment) selection = trtm; } tree.layout(); if (selection != null) { tree.select(selection); tree.setTopItem(selection); } else { tree.select(tree.getItem(0)); } } private void updateData() { txtDesc.setText(result == null ? "" : result.getDescription()); } public static AugmentSelectionDialog getInstance() { return instance; } public void dispose() { Manager.unhookHotkeys(shell); shell.dispose(); instance = null; } public boolean isActive() { return !shell.isDisposed() && shell.isVisible(); } private class AugmentIterator implements Iterator<AugmentObject> { private final ArrayList<AugmentObject> list; private final AugmentComparator comparator; private AugmentObject current = null; public AugmentIterator(ArrayList<AugmentObject> list, boolean byBlueprint) { comparator = new AugmentComparator(byBlueprint); if (filter == defaultFilter) { this.list = list; } else { this.list = new ArrayList<AugmentObject>(); for (AugmentObject a : list) { if (filter.accept(a)) this.list.add(a); } } } private AugmentObject getSmallestElement() { AugmentObject result = null; for (AugmentObject augment : list) { if (result == null || comparator.compare(augment, result) < 0) result = augment; } return result; } public void first() { current = getSmallestElement(); } public AugmentObject current() { return current; } @Override public boolean hasNext() { return !list.isEmpty(); } @Override public AugmentObject next() { remove(); current = getSmallestElement(); return current; } @Override public void remove() { list.remove(current); } } private class AugmentComparator implements Comparator<AugmentObject> { private final boolean byBlueprint; public AugmentComparator(boolean byBlueprint) { this.byBlueprint = byBlueprint; } @Override public int compare(AugmentObject o1, AugmentObject o2) { if (byBlueprint) { // Just compare the two blueprints together for alphanumerical ordering return o1.getBlueprintName().compareTo(o2.getBlueprintName()); } else { int result = o1.getTitle().compareTo(o2.getTitle()); if (result == 0) // If titles are the same, fall back to sorting by blueprint result = o1.getBlueprintName().compareTo(o2.getBlueprintName()); return result; } } } }