blob: aeec1ea2b987086ea34238dd515644236afca4e3 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2017 Exyte
*
* 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:
* Yuri Strot - initial API and Implementation
*******************************************************************************/
package org.eclipse.ui.glance.panels;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.core.runtime.Path;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.dialogs.PopupDialog;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
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.Menu;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.PreferencesUtil;
import org.eclipse.ui.glance.internal.GlanceEventDispatcher;
import org.eclipse.ui.glance.internal.GlancePlugin;
import org.eclipse.ui.glance.internal.panels.CheckAction;
import org.eclipse.ui.glance.internal.panels.ImageAnimation;
import org.eclipse.ui.glance.internal.preferences.IPreferenceConstants;
import org.eclipse.ui.glance.internal.search.SearchManager;
import org.eclipse.ui.glance.internal.search.SearchRule;
import org.eclipse.ui.glance.sources.Match;
import org.eclipse.ui.glance.utils.SelectionAdapter;
import org.eclipse.ui.glance.utils.UIUtils;
/**
* @author Yuri Strot
*
*/
public abstract class SearchPanel implements ISearchPanel,
IPreferenceConstants, IPropertyChangeListener {
/**
* @param parent
* @param style
*/
public SearchPanel() {
rule = new SearchRule("");
getPreferences().addPropertyChangeListener(this);
}
public void setIndexingState(final int state) {
indexState = state;
if (updateInfoThread != null) {
final UpdateInfoThread thread = updateInfoThread;
thread.requestStop();
updateInfoThread = null;
}
if (state == INDEXING_STATE_IN_PROGRESS) {
indexPercent = 0;
try {
updateInfoThread = new UpdateInfoThread();
updateInfoThread.start();
} catch (final CoreException e) {
GlancePlugin.log(e.getStatus());
}
} else {
UIUtils.asyncExec(toolBar, new Runnable() {
public void run() {
updateInfo(null);
}
});
}
}
public void updateIndexingPercent(final double percent) {
indexPercent = percent;
}
public void newTask(final String name) {
this.taskName = name;
indexPercent = 0;
}
private static URL getWaitImageStream() throws CoreException {
URL url = FileLocator.find(GlancePlugin.getDefault().getBundle(),
new Path(GlancePlugin.IMG_WAIT), null);
try {
url = FileLocator.resolve(url);
return url;
} catch (final IOException e) {
throw new CoreException(GlancePlugin.createStatus(
"Can't find wait image", e));
}
}
private Color getWaitBGColor() {
final Display display = PlatformUI.getWorkbench().getDisplay();
final Color[] color = new Color[1];
display.syncExec(new Runnable() {
public void run() {
color[0] = display.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND);
}
});
return color[0];
}
private class UpdateInfoThread extends ImageAnimation {
public UpdateInfoThread() throws CoreException {
super(getWaitImageStream(), getWaitBGColor());
}
private volatile boolean stop = false;
@Override
protected boolean isTerminated() {
return stop || indexState != INDEXING_STATE_IN_PROGRESS;
}
@Override
protected void updateImage(final Image image) {
updateInfo(image);
}
public void requestStop() {
stop = true;
}
}
private void updateInfo(final Image image) {
if (bIndexing == null || bIndexing.isDisposed()) {
return;
}
if (indexState == INDEXING_STATE_DISABLE) {
bIndexing.setToolTipText("Index component");
bIndexing.setSelection(false);
bIndexing.setEnabled(false);
if (bIndexing.getImage() == null) {
bIndexing.setImage(GlancePlugin
.getImage(GlancePlugin.IMG_START_INDEXING));
}
} else if (indexState == INDEXING_STATE_INITIAL) {
bIndexing.setToolTipText("Index component");
bIndexing.setSelection(false);
bIndexing.setImage(GlancePlugin
.getImage(GlancePlugin.IMG_START_INDEXING));
bIndexing.setEnabled(true);
} else if (indexState == INDEXING_STATE_FINISHED) {
bIndexing.setToolTipText("Index finished");
bIndexing.setSelection(false);
bIndexing.setEnabled(false);
} else {
final StringBuffer buffer = new StringBuffer();
bIndexing.setSelection(true);
bIndexing.setImage(image);
if (taskName != null && taskName.length() > 0) {
buffer.append(taskName);
buffer.append(": ");
}
buffer.append((int) (indexPercent * 100));
buffer.append("%. Stop indexing.");
bIndexing.setToolTipText(buffer.toString());
}
}
public void propertyChange(final PropertyChangeEvent event) {
final String property = event.getProperty();
if (property != null && property.startsWith(SEARCH_PREFIX)) {
updateRule();
}
}
public void createContent(final Composite parent) {
final Composite container = createContainer(parent);
final GridLayout layout = new GridLayout(3, false);
layout.verticalSpacing = 0;
layout.marginHeight = 2;
layout.marginWidth = 0;
container.setLayout(layout);
createIcon(container);
createText(container, SWT.BORDER);
createToolBar(container);
initSize(container);
}
public Control getControl() {
return container;
}
protected Composite createContainer(final Composite parent) {
container = new Composite(parent, SWT.NONE);
return container;
}
public void firstFound(final Match match) {
UIUtils.asyncExec(title, new Runnable() {
public void run() {
setBackground(match != null);
}
});
}
public void allFound(final Match[] matches) {
result = matches;
UIUtils.asyncExec(title, new Runnable() {
public void run() {
setBackground(result.length > 0);
}
});
}
public void finished() {
}
protected Label createIcon(final Composite parent) {
final Label label = new Label(parent, SWT.NONE);
label.setImage(GlancePlugin.getImage(GlancePlugin.IMG_SEARCH));
return label;
}
protected Control createText(final Composite parent, final int style) {
title = new Combo(parent, style | SWT.DROP_DOWN);
final String text = rule.getText();
title.setText(text);
loadHistory();
if (text != null && text.length() > 0 && result.length == 0)
setBackground(false);
title.addModifyListener(modifyListener);
title.addListener(SWT.KeyDown, new Listener() {
public void handleEvent(Event event) {
GlanceEventDispatcher.INSTANCE.dispatchKeyPressed(event);
}
});
title.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
title.setEnabled(titleEnabled);
return title;
}
protected ToolBar createToolBar(final Composite parent) {
toolBar = new ToolBar(parent, SWT.FLAT);
GridDataFactory.fillDefaults().align(SWT.END, SWT.CENTER)
.applyTo(toolBar);
if (getPreferences().getBoolean(PANEL_DIRECTIONS)) {
createNextItem(toolBar);
createPreviousItem(toolBar);
}
createIndexing(toolBar);
createSettingsMenu(toolBar);
if (getPreferences().getBoolean(PANEL_CLOSE)) {
createClose(toolBar);
}
return toolBar;
}
protected ToolItem createNextItem(final ToolBar bar) {
bNext = createTool(bar, "Next", GlancePlugin.IMG_NEXT,
new SelectionAdapter() {
@Override
public void selected(final SelectionEvent e) {
findNext();
}
});
return bNext;
}
protected ToolItem createPreviousItem(final ToolBar bar) {
bPrev = createTool(bar, "Previous", GlancePlugin.IMG_PREV,
new SelectionAdapter() {
@Override
public void selected(final SelectionEvent e) {
findPrevious();
}
});
return bPrev;
}
private void createIndexing(final ToolBar bar) {
bIndexing = new ToolItem(bar, SWT.CHECK);
bIndexing.setDisabledImage(GlancePlugin
.getImage(GlancePlugin.IMG_INDEXING_FINISHED));
bIndexing.addSelectionListener(new SelectionAdapter() {
@Override
public void selected(final SelectionEvent e) {
if (indexState == INDEXING_STATE_INITIAL) {
SearchManager.getIntance().index();
} else if (indexState != INDEXING_STATE_FINISHED) {
fireIndexCanceled();
}
}
});
}
private ToolItem createTool(final ToolBar bar, final String tip,
final String image, final SelectionListener listener) {
final ToolItem item = new ToolItem(bar, SWT.PUSH);
item.setToolTipText(tip);
item.setImage(GlancePlugin.getImage(image));
item.addSelectionListener(listener);
return item;
}
protected ToolItem createSettingsMenu(final ToolBar bar) {
final ToolItem settings = new ToolItem(bar, SWT.PUSH);
settings.setImage(JFaceResources.getImage(PopupDialog.POPUP_IMG_MENU));
settings.setDisabledImage(JFaceResources
.getImage(PopupDialog.POPUP_IMG_MENU_DISABLED));
settings.setToolTipText("Settings"); //$NON-NLS-1$
settings.addSelectionListener(new SelectionAdapter() {
@Override
public void selected(final SelectionEvent e) {
showSettings();
}
});
return settings;
}
protected ToolItem createClose(final ToolBar bar) {
final ToolItem close = new ToolItem(bar, SWT.PUSH);
final ImageDescriptor image = PlatformUI.getWorkbench()
.getSharedImages()
.getImageDescriptor(ISharedImages.IMG_TOOL_DELETE);
if (image != null)
close.setImage(image.createImage());
close.setToolTipText("Close"); //$NON-NLS-1$
close.addSelectionListener(new SelectionAdapter() {
@Override
public void selected(final SelectionEvent e) {
closePanel();
}
});
return close;
}
protected void showSettings() {
final MenuManager manager = new MenuManager();
fillMenu(manager);
final Menu menu = manager.createContextMenu(getControl());
final Point location = getControl().getDisplay().getCursorLocation();
menu.setLocation(location);
menu.setVisible(true);
}
protected void fillMenu(final IMenuManager menu) {
menu.add(new Separator());
newAction(menu, SEARCH_CASE_SENSITIVE, LABEL_CASE_SENSITIVE, true);
final boolean regExp = newAction(menu, SEARCH_REGEXP, LABEL_REGEXP,
true).isChecked();
newAction(menu, SEARCH_CAMEL_CASE, LABEL_CAMEL_CASE, !regExp);
newAction(menu, SEARCH_WORD_PREFIX, LABEL_WORD_PREFIX, !regExp);
menu.add(new Separator());
menu.add(new Action("Clear search history") {
@Override
public void run() {
clearHistory();
}
});
menu.add(new Separator());
menu.add(new Action("Preferences...") {
@Override
public void run() {
PreferencesUtil.createPreferenceDialogOn(container.getShell(),
PREFERENCE_PAGE_ID, null, null).open();
}
});
}
private CheckAction newAction(final IMenuManager menu, final String name,
final String label, final boolean enable) {
return newAction(menu, name, label, enable, null);
}
private CheckAction newAction(final IMenuManager menu, final String name,
final String label, final boolean enable, final String path) {
final CheckAction action = new CheckAction(name, label);
if (path != null) {
action.setImageDescriptor(GlancePlugin.getImageDescriptor(path));
}
action.setEnabled(enable);
menu.add(action);
return action;
}
protected void textChanged() {
final String text = title.getText();
final boolean empty = text.length() == 0;
if (empty)
textEmpty();
if (bNext != null && !bNext.isDisposed())
bNext.setEnabled(!empty);
if (bPrev != null && !bPrev.isDisposed())
bPrev.setEnabled(!empty);
updateRule();
}
protected void textEmpty() {
setBackground(true);
}
protected void updateRule() {
rule = new SearchRule(title.getText());
fireRuleChanged(rule);
}
/**
* @return the rule
*/
public SearchRule getRule() {
return rule;
}
public void setFocus(String text) {
if (isReady()) {
if (text == null || text.length() == 0)
text = rule.getText();
if (text != null && text.length() > 0) {
title.setText(text);
title.setSelection(new Point(0, text.length()));
textChanged();
}
title.forceFocus();
}
}
public void setEnabled(final boolean enabled) {
if (isReady()) {
title.setEnabled(enabled);
titleEnabled = enabled;
}
}
private boolean isReady() {
return title != null && !title.isDisposed();
}
public void addPanelListener(final ISearchPanelListener listener) {
listeners.add(listener);
}
public void removePanelListener(final ISearchPanelListener listener) {
listeners.remove(listener);
}
private void fireRuleChanged(final SearchRule rule) {
historyDirty = true;
final Object[] objects = listeners.getListeners();
for (final Object object : objects) {
final ISearchPanelListener listener = (ISearchPanelListener) object;
listener.ruleChanged(rule);
}
}
protected void fireIndexCanceled() {
final Object[] objects = listeners.getListeners();
for (final Object object : objects) {
final ISearchPanelListener listener = (ISearchPanelListener) object;
listener.indexCanceled();
}
}
public void findNext() {
updateHistory();
final Object[] objects = listeners.getListeners();
for (final Object object : objects) {
final ISearchPanelListener listener = (ISearchPanelListener) object;
listener.findNext();
}
}
public void findPrevious() {
updateHistory();
final Object[] objects = listeners.getListeners();
for (final Object object : objects) {
final ISearchPanelListener listener = (ISearchPanelListener) object;
listener.findPrevious();
}
}
public void clearHistory() {
if (title != null && !title.isDisposed()) {
title.removeModifyListener(modifyListener);
try {
title.removeAll();
findHistory.clear();
} finally {
title.addModifyListener(modifyListener);
historyDirty = false;
}
}
}
protected void fireClose() {
updateHistory();
saveHistory();
final Object[] objects = listeners.getListeners();
for (final Object object : objects) {
final ISearchPanelListener listener = (ISearchPanelListener) object;
listener.close();
}
getPreferences().removePropertyChangeListener(this);
if (updateInfoThread != null) {
updateInfoThread.requestStop();
}
}
/**
* @return the preferedWidth
*/
protected int getPreferedWidth() {
return preferredWidth;
}
/**
* @return the preferredHeight
*/
protected int getPreferredHeight() {
return preferredHeight;
}
private void initSize(final Composite composite) {
final Point size = composite.computeSize(SWT.DEFAULT, SWT.DEFAULT);
preferredWidth = size.x;
preferredHeight = size.y;
preferredWidth -= title.computeSize(SWT.DEFAULT, SWT.DEFAULT).x;
final int widthInChars = getPreferences().getInt(PANEL_TEXT_SIZE);
preferredWidth += getTextWidth(title, widthInChars) + 15;
}
protected int getTextWidth(final Control control, final int width) {
final GC gc = new GC(control);
try {
gc.setFont(title.getFont());
return gc.getFontMetrics().getAverageCharWidth() * width;
} finally {
gc.dispose();
}
}
/**
* Updates history.
*/
private void updateHistory() {
if (title != null && !title.isDisposed() && historyDirty) {
title.removeModifyListener(modifyListener);
try {
String findString = title.getText();
final Point sel = title.getSelection();
findString = fixItem(findString);
if (findString != null) {
findHistory.remove(findString);
findHistory.add(0, findString);
title.removeAll();
for (final String string : findHistory) {
title.add(string);
}
title.setText(findString);
title.setSelection(sel);
}
} finally {
title.addModifyListener(modifyListener);
historyDirty = false;
}
}
}
private void saveHistory() {
final StringBuffer buffer = new StringBuffer();
for (int i = 0; i < findHistory.size() && i < 8; i++) {
final String item = findHistory.get(i);
if (i > 0) {
buffer.append("\n");
}
buffer.append(item);
}
getPreferences().putValue(HISTORY, buffer.toString());
}
private void loadHistory() {
findHistory = new ArrayList<String>();
final String content = getPreferences().getString(HISTORY);
final String[] items = content.split("\n");
for (final String item : items) {
findHistory.add(item);
title.add(item);
}
}
private String fixItem(final String item) {
if (item.length() == 0) {
return null;
}
final int index = item.indexOf("\n");
if (index == 0) {
return null;
} else if (index > 0) {
return item.substring(0, index);
} else {
return item;
}
}
private IPreferenceStore getPreferences() {
return GlancePlugin.getDefault().getPreferenceStore();
}
protected void setBackground(final boolean found) {
title.setBackground(found ? GOOD_COLOR : BAD_COLOR);
}
protected static final Color GOOD_COLOR = Display.getDefault()
.getSystemColor(SWT.COLOR_WHITE);
protected static final Color BAD_COLOR = new Color(Display.getDefault(),
255, 102, 102);
protected Composite container;
protected Combo title;
private boolean titleEnabled = true;
private final ListenerList listeners = new ListenerList();
private final ModifyListener modifyListener = new ModifyListener() {
public void modifyText(final ModifyEvent e) {
textChanged();
}
};
private List<String> findHistory;
private boolean historyDirty = true;
private ToolItem bNext;
private ToolItem bPrev;
private ToolItem bIndexing;
private ToolBar toolBar;
private SearchRule rule;
private Match[] result = Match.EMPTY;
private double indexState;
private double indexPercent;
private String taskName;
private int preferredHeight;
private int preferredWidth;
private UpdateInfoThread updateInfoThread;
}