package rocks.inspectit.ui.rcp.editor.search;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.ShellAdapter;
import org.eclipse.swt.events.ShellEvent;
import org.eclipse.swt.events.TraverseEvent;
import org.eclipse.swt.events.TraverseListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.FontMetrics;
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.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IPartListener;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.forms.FormColors;
import org.eclipse.ui.forms.widgets.FormToolkit;
import rocks.inspectit.shared.all.util.ObjectUtils;
import rocks.inspectit.ui.rcp.InspectIT;
import rocks.inspectit.ui.rcp.InspectITImages;
import rocks.inspectit.ui.rcp.editor.search.criteria.SearchCriteria;
import rocks.inspectit.ui.rcp.editor.search.criteria.SearchResult;
/**
* Control that is displayed for search purposes.
*
* @author Ivan Senic
*
*/
public class SearchControl {
/**
* {@link rocks.inspectit.ui.rcp.editor.ISubView} to notify about search.
*/
private ISearchExecutor searchExecutor;
/**
* Main composite for showing the components.
*/
private Composite mainComposite;
/**
* Search text box.
*/
private Text searchTextBox;
/**
* Is case sensitive.
*/
private ToolItem caseSensitiveButton;
/**
* Close button.
*/
private ToolItem closeButton;
/**
* Shell that we will create to display the search.
*/
private Shell shell;
/**
* Next button.
*/
private ToolItem next;
/**
* Previous button.
*/
private ToolItem previous;
/**
* Last {@link SearchResult}.
*/
private SearchResult lastSearchResult;
/**
* Default constructor.
*
* @param searchExecutor
* The implementation that will be pass the search string.
* @param parentShell
* Shell where the search will be created.
* @param paintRelativeControl
* Control where the paint of the search box should occur.
* @param editor
* Editor where the search will be painted. This is needed because of the editor
* closing, hiding, etc action.
*/
public SearchControl(ISearchExecutor searchExecutor, Shell parentShell, Control paintRelativeControl, IEditorPart editor) {
this.searchExecutor = searchExecutor;
createSearchShell(parentShell, paintRelativeControl, editor);
}
/**
* Creates the control. The control will be painted in the top-right corner of the
* controlToPaint.
*
* @param parentShell
* Parent shell.
* @param paintRelativeControl
* Control where the paint of the search box should occur.
* @param editor
* Editor where the search will be painted. This is needed because of the editor
* closing, hiding, etc action.
*/
private void createSearchShell(Shell parentShell, final Control paintRelativeControl, final IEditorPart editor) {
Display display = Display.getDefault();
FormColors formColors = new FormColors(display);
FormToolkit toolkit = new FormToolkit(formColors);
shell = new Shell(parentShell, SWT.BORDER | SWT.TOOL);
mainComposite = toolkit.createComposite(shell);
GridLayout layout = new GridLayout(3, false);
mainComposite.setLayout(layout);
mainComposite.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false));
searchTextBox = toolkit.createText(mainComposite, null, SWT.BORDER);
GridData gd = new GridData();
gd.grabExcessHorizontalSpace = true;
gd.horizontalAlignment = GridData.FILL;
gd.minimumWidth = 200;
searchTextBox.setLayoutData(gd);
searchTextBox.addTraverseListener(new TraverseListener() {
@Override
public void keyTraversed(TraverseEvent e) {
if (e.detail == SWT.TRAVERSE_RETURN) {
executeSearch();
}
}
});
searchTextBox.addPaintListener(new PaintListener() {
@Override
public void paintControl(PaintEvent e) {
if (null != lastSearchResult) {
String string = lastSearchResult.getCurrentOccurence() + " of " + lastSearchResult.getTotalOccurrences();
if (lastSearchResult.getTotalOccurrences() > 0) {
paintString(e, string, Display.getDefault().getSystemColor(SWT.COLOR_GRAY));
} else {
paintString(e, string, Display.getDefault().getSystemColor(SWT.COLOR_RED));
}
}
}
private void paintString(PaintEvent e, String string, Color color) {
Point point = searchTextBox.getSize();
FontMetrics fontMetrics = e.gc.getFontMetrics();
int width = fontMetrics.getAverageCharWidth() * string.length();
int height = fontMetrics.getHeight();
e.gc.setForeground(color);
e.gc.drawString(string, point.x - width - searchTextBox.getBorderWidth() - 2, (point.y - height - (searchTextBox.getBorderWidth() * 2)) / 2, true);
}
});
ToolBar toolBar = new ToolBar(mainComposite, SWT.FLAT);
toolBar.setBackground(formColors.getBackground());
previous = new ToolItem(toolBar, SWT.PUSH | SWT.NO_BACKGROUND);
previous.setImage(InspectIT.getDefault().getImage(InspectITImages.IMG_PREVIOUS));
previous.setEnabled(false);
previous.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
executePrevious();
}
});
next = new ToolItem(toolBar, SWT.PUSH | SWT.NO_BACKGROUND);
next.setImage(InspectIT.getDefault().getImage(InspectITImages.IMG_NEXT));
next.setEnabled(false);
next.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
executeNext();
}
});
caseSensitiveButton = new ToolItem(toolBar, SWT.CHECK | SWT.NO_BACKGROUND);
caseSensitiveButton.setImage(InspectIT.getDefault().getImage(InspectITImages.IMG_FONT));
caseSensitiveButton.setToolTipText("Case sensitive");
caseSensitiveButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
executeSearch();
}
});
// added additional composite to the right, so that minimizing and maximizing the window
// can look better
Composite helpComposite = toolkit.createComposite(mainComposite);
gd = new GridData();
gd.grabExcessHorizontalSpace = true;
gd.horizontalAlignment = GridData.FILL;
gd.minimumWidth = 0;
gd.heightHint = 0;
gd.widthHint = 0;
helpComposite.setLayoutData(gd);
closeButton = new ToolItem(toolBar, SWT.PUSH | SWT.NO_BACKGROUND);
closeButton.setImage(InspectIT.getDefault().getImage(InspectITImages.IMG_CLOSE));
closeButton.setToolTipText("Close");
closeButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
searchExecutor.clearSearch();
closeControl();
}
});
KeyAdapter keyCloseAdapter = new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.keyCode == SWT.ESC) {
searchExecutor.clearSearch();
closeControl();
}
}
};
mainComposite.addKeyListener(keyCloseAdapter);
for (Control child : mainComposite.getChildren()) {
child.addKeyListener(keyCloseAdapter);
}
searchTextBox.forceFocus();
mainComposite.pack();
mainComposite.setSize(mainComposite.computeSize(SWT.DEFAULT, SWT.DEFAULT));
final Point shellSize = shell.computeSize(SWT.DEFAULT, SWT.DEFAULT);
Point controlPosition = paintRelativeControl.toDisplay(0, 0);
Point controlSize = paintRelativeControl.getSize();
int xPosition = (controlPosition.x + controlSize.x) - shellSize.x - paintRelativeControl.getBorderWidth();
int yPosition = (controlPosition.y + controlSize.y) - shellSize.y - paintRelativeControl.getBorderWidth();
shell.setLocation(xPosition, yPosition);
shell.setSize(shellSize);
shell.open();
paintRelativeControl.addControlListener(new ControlAdapter() {
@Override
public void controlResized(ControlEvent e) {
if (!shell.isDisposed()) {
Point controlPosition = paintRelativeControl.toDisplay(0, 0);
Point controlSize = paintRelativeControl.getSize();
int xPosition = (controlPosition.x + controlSize.x) - shellSize.x - paintRelativeControl.getBorderWidth();
int yPosition = (controlPosition.y + controlSize.y) - shellSize.y - paintRelativeControl.getBorderWidth();
shell.setLocation(xPosition, yPosition);
}
}
});
editor.getSite().getPage().addPartListener(new IPartListener() {
@Override
public void partOpened(IWorkbenchPart part) {
}
@Override
public void partDeactivated(IWorkbenchPart part) {
if (ObjectUtils.equals(editor, part)) {
closeControl();
}
}
@Override
public void partClosed(IWorkbenchPart part) {
if (ObjectUtils.equals(editor, part)) {
closeControl();
}
}
@Override
public void partBroughtToTop(IWorkbenchPart part) {
}
@Override
public void partActivated(IWorkbenchPart part) {
}
});
OpenedSearchControlCache.register(searchExecutor, this);
shell.addShellListener(new ShellAdapter() {
@Override
public void shellClosed(ShellEvent e) {
searchExecutor.clearSearch();
OpenedSearchControlCache.unregister(searchExecutor);
}
});
toolkit.dispose();
}
/**
* Closes the control.
*/
public final void closeControl() {
if (!shell.isDisposed()) {
shell.close();
}
}
/**
* Executes the search.
*/
private void executeSearch() {
String searchString = searchTextBox.getText().trim();
if (!searchString.isEmpty()) {
SearchCriteria searchCriteria = new SearchCriteria(searchString, caseSensitiveButton.getSelection());
lastSearchResult = searchExecutor.executeSearch(searchCriteria);
processSearchResult(lastSearchResult);
} else {
searchExecutor.clearSearch();
lastSearchResult = null; // NOPMD
searchTextBox.redraw();
}
}
/**
* Executes next functionality.
*/
private void executeNext() {
lastSearchResult = searchExecutor.next();
processSearchResult(lastSearchResult);
}
/**
* Execute previous functionality.
*/
private void executePrevious() {
lastSearchResult = searchExecutor.previous();
processSearchResult(lastSearchResult);
}
/**
* Processes the {@link SearchResult}.
*
* @param result
* {@link SearchResult}.
*/
private void processSearchResult(SearchResult result) {
searchTextBox.redraw();
if (null != result) {
next.setEnabled(result.isCanShowNext());
previous.setEnabled(result.isCanShowPrevious());
}
}
}