blob: 7957885247f0a39b237b18cb3f50f19159d7eb9b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010 BestSolution.at and others.
* 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:
* Tom Schindl <tom.schindl@bestsolution.at> - initial API and implementation
* Steven Spungin <steven@spungin.tv> - Bug 404136
******************************************************************************/
package org.eclipse.e4.tools.emf.ui.internal.common.component.dialogs;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import org.eclipse.core.databinding.observable.list.IObservableList;
import org.eclipse.core.databinding.observable.list.WritableList;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceVisitor;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.e4.tools.emf.ui.internal.Messages;
import org.eclipse.e4.tools.emf.ui.internal.StringMatcher;
import org.eclipse.e4.ui.model.application.MApplicationElement;
import org.eclipse.emf.common.command.Command;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.edit.command.SetCommand;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.jface.databinding.viewers.ObservableListContentProvider;
import org.eclipse.jface.dialogs.TitleAreaDialog;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StyledCellLabelProvider;
import org.eclipse.jface.viewers.StyledString;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
public abstract class AbstractIconDialog extends TitleAreaDialog {
private TableViewer viewer;
private IProject project;
private MApplicationElement element;
private EStructuralFeature feature;
private EditingDomain editingDomain;
private Map<IFile, Image> icons = Collections.synchronizedMap(new HashMap<IFile, Image>());
private SearchScope searchScope = SearchScope.PROJECT;
protected Messages Messages;
private Text textFilter;
public AbstractIconDialog(Shell parentShell, IProject project, EditingDomain editingDomain, MApplicationElement element, EStructuralFeature feature, Messages Messages) {
super(parentShell);
this.editingDomain = editingDomain;
this.element = element;
this.feature = feature;
this.project = project;
this.Messages = Messages;
}
protected abstract String getShellTitle();
protected abstract String getDialogTitle();
protected abstract String getDialogMessage();
@Override
protected Control createDialogArea(Composite parent) {
Composite comp = (Composite) super.createDialogArea(parent);
getShell().setText(getShellTitle());
setTitle(getDialogTitle());
setMessage(getDialogMessage());
Composite container = new Composite(comp, SWT.NONE);
container.setLayoutData(new GridData(GridData.FILL_BOTH));
container.setLayout(new GridLayout(2, false));
Label lblScope = new Label(container, SWT.NONE);
lblScope.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, false, false));
lblScope.setText(Messages.AbstractIconDialog_scope);
Composite compOptions = new Composite(container, SWT.NONE);
compOptions.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
compOptions.setLayout(new RowLayout());
Button btnProject = new Button(compOptions, SWT.RADIO);
btnProject.setText(Messages.AbstractIconDialog_current_project);
btnProject.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
searchScope = SearchScope.PROJECT;
textFilter.notifyListeners(SWT.Modify, new Event());
}
});
btnProject.setSelection(true);
Button btnWorkspace = new Button(compOptions, SWT.RADIO);
btnWorkspace.setText(Messages.AbstractIconDialog_all_workspace_bundles);
btnWorkspace.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
searchScope = SearchScope.WORKSPACE;
textFilter.notifyListeners(SWT.Modify, new Event());
}
});
btnWorkspace.setSelection(searchScope == SearchScope.WORKSPACE);
btnProject.setSelection(searchScope == SearchScope.PROJECT);
Label l = new Label(container, SWT.NONE);
l.setText(Messages.AbstractIconDialog_IconName);
textFilter = new Text(container, SWT.BORDER | SWT.SEARCH | SWT.ICON_SEARCH);
textFilter.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
new Label(container, SWT.NONE);
viewer = new TableViewer(container);
GridData gd = new GridData(GridData.FILL_BOTH);
viewer.getControl().setLayoutData(gd);
viewer.setContentProvider(new ObservableListContentProvider());
viewer.setLabelProvider(new StyledCellLabelProvider() {
@Override
public void update(ViewerCell cell) {
IFile file = (IFile) cell.getElement();
StyledString styledString = new StyledString(file.getProjectRelativePath().toString(), null);
Image img = icons.get(file);
if (img == null) {
InputStream in = null;
try {
in = file.getContents();
img = new Image(cell.getControl().getDisplay(), in);
icons.put(file, img);
} catch (CoreException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
String bundle = getBundle(file);
if (bundle != null) {
styledString.append(" - " + bundle, StyledString.DECORATIONS_STYLER); //$NON-NLS-1$
}
cell.setImage(img);
cell.setText(styledString.getString());
cell.setStyleRanges(styledString.getStyleRanges());
}
});
final WritableList list = new WritableList();
viewer.setInput(list);
viewer.addDoubleClickListener(new IDoubleClickListener() {
@Override
public void doubleClick(DoubleClickEvent event) {
okPressed();
}
});
textFilter.addModifyListener(new ModifyListener() {
private IconMatchCallback callback;
private Timer timer = new Timer(true);
private TimerTask task;
@Override
public void modifyText(ModifyEvent e) {
if (callback != null) {
callback.cancel = true;
}
if (task != null) {
task.cancel();
}
list.clear();
clearImages();
callback = new IconMatchCallback(list);
task = new SearchThread(callback, textFilter.getText(), project, searchScope);
timer.schedule(task, 500);
}
});
getShell().addDisposeListener(new DisposeListener() {
@Override
public void widgetDisposed(DisposeEvent e) {
clearImages();
}
});
return comp;
}
private void clearImages() {
for (Image img : icons.values()) {
img.dispose();
}
icons.clear();
}
@Override
protected boolean isResizable() {
return true;
}
@Override
protected void okPressed() {
IStructuredSelection s = (IStructuredSelection) viewer.getSelection();
if (!s.isEmpty()) {
IFile file = (IFile) s.getFirstElement();
String bundle = getBundle(file);
String uri = "platform:/plugin/" + bundle + "/" + file.getProjectRelativePath().toString(); //$NON-NLS-1$//$NON-NLS-2$
Command cmd = SetCommand.create(editingDomain, element, feature, uri);
if (cmd.canExecute()) {
editingDomain.getCommandStack().execute(cmd);
super.okPressed();
}
}
}
private String getBundle(IFile file) {
IProject project = file.getProject();
IFile f = project.getFile("/META-INF/MANIFEST.MF"); //$NON-NLS-1$
if (f != null && f.exists()) {
BufferedReader r = null;
try {
InputStream s = f.getContents();
r = new BufferedReader(new InputStreamReader(s));
String line;
while ((line = r.readLine()) != null) {
if (line.startsWith("Bundle-SymbolicName:")) { //$NON-NLS-1$
int start = line.indexOf(':');
int end = line.indexOf(';');
if (end == -1) {
end = line.length();
}
return line.substring(start + 1, end).trim();
}
}
} catch (CoreException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (r != null) {
try {
r.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
return null;
}
private class IconMatchCallback {
private volatile boolean cancel;
private IObservableList list;
private IconMatchCallback(IObservableList list) {
this.list = list;
}
public void match(final IFile file) {
if (!cancel) {
list.getRealm().exec(new Runnable() {
@Override
public void run() {
list.add(file);
}
});
}
}
}
private static class SearchThread extends TimerTask {
private final IconMatchCallback callback;
private final IProject project;
private final StringMatcher matcherGif;
private final StringMatcher matcherJpg;
private final StringMatcher matcherPng;
private SearchScope searchScope;
public SearchThread(IconMatchCallback callback, String pattern, IProject project, SearchScope searchScope) {
this.matcherGif = new StringMatcher("*" + pattern + "*.gif", true, false); //$NON-NLS-1$//$NON-NLS-2$
this.matcherJpg = new StringMatcher("*" + pattern + "*.jpg", true, false); //$NON-NLS-1$//$NON-NLS-2$
this.matcherPng = new StringMatcher("*" + pattern + "*.png", true, false); //$NON-NLS-1$//$NON-NLS-2$
this.callback = callback;
this.project = project;
this.searchScope = searchScope;
}
@Override
public void run() {
List<IProject> projects;
switch (searchScope) {
case WORKSPACE:
projects = Arrays.asList(project.getWorkspace().getRoot().getProjects());
break;
case PROJECT:
default:
projects = Arrays.asList(project);
break;
}
try {
for (IProject project : projects) {
// Only search bundles
if (project.getFile("/META-INF/MANIFEST.MF").exists() == false) { //$NON-NLS-1$
continue;
}
project.accept(new IResourceVisitor() {
@Override
public boolean visit(IResource resource) throws CoreException {
if (callback.cancel) {
return false;
}
if (resource.getType() == IResource.FOLDER || resource.getType() == IResource.PROJECT) {
return true;
} else if (resource.getType() == IResource.FILE && !resource.isLinked()) {
String path = resource.getProjectRelativePath().toString();
if (matcherGif.match(path) || matcherPng.match(path) || matcherJpg.match(path)) {
callback.match((IFile) resource);
}
}
return false;
}
});
}
} catch (CoreException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}