package rocks.inspectit.ui.rcp.filter; import java.util.Objects; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.swt.SWT; import org.eclipse.swt.events.FocusAdapter; import org.eclipse.swt.events.FocusEvent; import org.eclipse.swt.events.KeyAdapter; import org.eclipse.swt.events.KeyEvent; 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.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.progress.UIJob; /** * A composite that displays a text box with {@link SWT#ICON_SEARCH} and {@link SWT#ICON_CANCEL} * properties. The text box has a default text that is displayed in gray color. This text can be * defined in the constructor. * <p> * The subclasses should implement two methods, one for executing the filter and one for canceling * the filtering. * * @author Ivan Senic * */ public abstract class FilterComposite extends Composite { /** * Time in milliseconds that will be used to wait for another input character before filter is * executed. */ private static final int FILTER_KEYRELEASED_DELAY = 300; /** * Boolean that will keep if the filter was executed. This will help to determine if the * subclasses should be informed via {@link #executeCancel()}. */ private boolean filterExecuted = false; /** * Filter text box. */ private Text filterText; /** * Default text. */ private String defaultText; /** * {@link UIJob} that is executing the filter with delay. */ private UIJob filterJob; /** * Default constructor. * * @param parent * A widget which will be the parent of the new instance (cannot be null). * @param style * The style of widget to construct. * @param defaultText * Text to be displayed in the text box when no filtering is active. * @see Composite#Composite(Composite, int) */ public FilterComposite(Composite parent, int style, String defaultText) { super(parent, style); this.defaultText = defaultText; filterJob = new UIJob("Filter") { @Override public IStatus runInUIThread(IProgressMonitor monitor) { executeFilterInternal(); return Status.OK_STATUS; } }; filterJob.setUser(false); init(); } /** * This method is called when filtering should be canceled. Subclasses should implement proper * actions. */ protected abstract void executeCancel(); /** * This method is called when filtering should occur. Subclasses should implement proper * actions. * * @param filterString * String that was entered as a criteria in the filter text box. */ protected abstract void executeFilter(String filterString); /** * {@inheritDoc} */ @Override public void setEnabled(boolean enabled) { super.setEnabled(enabled); filterText.setEnabled(enabled); } /** * Initializes the widget. */ private void init() { setLayout(new GridLayout(1, false)); filterText = new Text(this, SWT.SINGLE | SWT.BORDER | SWT.SEARCH | SWT.ICON_CANCEL | SWT.ICON_SEARCH); filterText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); setDefaultText(); filterText.addSelectionListener(new SelectionAdapter() { @Override public void widgetDefaultSelected(SelectionEvent e) { if (e.detail == SWT.CANCEL) { if (!Objects.equals(filterText.getText(), defaultText)) { executeCancelInternal(); } } } }); filterText.addFocusListener(new FocusAdapter() { @Override public void focusGained(FocusEvent e) { if (Objects.equals(filterText.getText(), defaultText)) { setEmptyText(); } } @Override public void focusLost(FocusEvent e) { if (Objects.equals(filterText.getText(), "")) { setDefaultText(); } } }); filterText.addTraverseListener(new TraverseListener() { @Override public void keyTraversed(TraverseEvent e) { if (e.detail == SWT.TRAVERSE_ESCAPE) { filterJob.cancel(); executeCancelInternal(); } else if (e.detail == SWT.TRAVERSE_RETURN) { filterJob.cancel(); executeFilterInternal(); } } }); filterText.addKeyListener(new KeyAdapter() { @Override public void keyReleased(KeyEvent e) { if (Character.isLetterOrDigit(e.character) || (e.keyCode == SWT.BS) || (e.keyCode == SWT.DEL)) { filterJob.cancel(); filterJob.schedule(FILTER_KEYRELEASED_DELAY); } } }); } /** * Executes cancel internally. */ private void executeCancelInternal() { if (filterExecuted) { filterJob.cancel(); executeCancel(); filterExecuted = false; } setDefaultText(); this.forceFocus(); } /** * Executes filter internally. */ private void executeFilterInternal() { String filterString = filterText.getText().trim(); executeFilter(filterString); filterExecuted = true; } /** * Sets default text in gray color. */ private void setDefaultText() { filterText.setText(defaultText); filterText.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_GRAY)); } /** * Empties the text box and set font color to black. */ private void setEmptyText() { filterText.setText(""); filterText.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK)); } }