--- /dev/null
+package org.simantics.scl.ui.modulebrowser;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.simantics.scl.ui.Activator;
+
+import gnu.trove.map.hash.THashMap;
+
+public class CreateModuleAction {
+ public static final String PREFIX = "reference:file:/";
+
+ public static final void createModule(Bundle bundle, String packageName, String moduleName) throws IOException {
+ String bundleLocation = bundle.getLocation();
+ bundleLocation = bundleLocation.substring(PREFIX.length());
+
+ Path packagePath = Paths.get(bundleLocation).resolve("scl").resolve(packageName);
+ Files.createDirectories(packagePath);
+ Path modulePath = packagePath.resolve(moduleName + ".scl");
+ if(Files.exists(modulePath))
+ return;
+ Files.createFile(modulePath);
+ }
+
+ public static THashMap<String,Bundle> findGoodBundles() {
+ BundleContext context = Activator.getInstance().getBundle().getBundleContext();
+ THashMap<String,Bundle> result = new THashMap<String,Bundle>();
+ for(Bundle bundle : context.getBundles()) {
+ String location = bundle.getLocation();
+ if(location.endsWith(".jar") || !location.startsWith(PREFIX))
+ continue;
+ location = location.substring(PREFIX.length());
+ Path bundlePath = Paths.get(location);
+ if(!Files.isDirectory(bundlePath))
+ continue;
+ result.put(bundle.getSymbolicName(), bundle);
+ }
+ return result;
+ }
+
+ public static String findBestPlugin(THashMap<String,Bundle> bundles, String packageName) {
+ for(Bundle bundle : bundles.values()) {
+ String location = bundle.getLocation();
+ location = location.substring(PREFIX.length());
+ Path packagePath = Paths.get(location).resolve("scl").resolve(packageName);
+ if(Files.exists(packagePath))
+ return bundle.getSymbolicName();
+ }
+ int p = packageName.lastIndexOf('/');
+ if(p == -1)
+ return "";
+ else
+ return findBestPlugin(bundles, packageName.substring(0, p));
+ }
+
+ public static int packageMatchLength(Bundle bundle, String packageName) {
+ if(bundle.getEntry("scl") == null)
+ return 0;
+ packageName = "scl/" + packageName;
+ while(packageName.length() > 3) {
+ if(bundle.getEntry(packageName) != null)
+ return packageName.length();
+ int p = packageName.lastIndexOf('/');
+ packageName = packageName.substring(0, p);
+ }
+ return 3;
+ }
+}
--- /dev/null
+package org.simantics.scl.ui.modulebrowser;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.ErrorDialog;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.layout.GridLayoutFactory;
+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.Color;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+import org.osgi.framework.Bundle;
+import org.simantics.scl.osgi.SCLOsgi;
+import org.simantics.scl.ui.editor2.OpenSCLModule;
+import org.simantics.scl.ui.modulebrowser.PluginSelectionDialog.Entry;
+
+import gnu.trove.map.hash.THashMap;
+
+public class CreateModuleDialog extends Dialog {
+
+ SCLModuleBrowser parentBrowser;
+
+ String initialPackageName = "";
+ String initialPluginName = "";
+
+ Text packageName;
+ Text moduleName;
+ Text pluginName;
+ private THashMap<String,Bundle> bundles;
+
+ private Color normalBackground;
+ private Color errorBackground;
+
+ protected CreateModuleDialog(Shell parentShell, SCLModuleBrowser parentBrowser) {
+ super(parentShell);
+ this.parentBrowser = parentBrowser;
+ setShellStyle(SWT.RESIZE | SWT.TITLE | SWT.BORDER);
+ bundles = CreateModuleAction.findGoodBundles();
+
+ normalBackground = parentShell.getDisplay().getSystemColor(SWT.COLOR_WHITE);
+ errorBackground = new Color(parentShell.getDisplay(), 255, 128, 128);
+ }
+
+ @Override
+ protected Control createDialogArea(Composite parent) {
+ getShell().setText("Create New Module");
+ getShell().addDisposeListener(new DisposeListener() {
+ @Override
+ public void widgetDisposed(DisposeEvent e) {
+ errorBackground.dispose();
+ }
+ });
+
+ final Composite composite = (Composite) super.createDialogArea(parent);
+ GridLayoutFactory.fillDefaults().margins(10,10).numColumns(2).applyTo(composite);
+ GridDataFactory.fillDefaults().grab(true,true).applyTo(composite);
+
+ // Package name
+ Label packageNameLabel = new Label(composite, SWT.NONE);
+ packageNameLabel.setText("Package");
+ GridDataFactory.fillDefaults().applyTo(packageNameLabel);
+
+ packageName = new Text(composite, SWT.BORDER);
+ GridDataFactory.fillDefaults().grab(true, false).minSize(500, SWT.DEFAULT).applyTo(packageName);
+ packageName.setText(initialPackageName);
+ packageName.addModifyListener(modifyListener);
+
+ // Module name
+ Label moduleNameLabel = new Label(composite, SWT.NONE);
+ moduleNameLabel.setText("Module name");
+ GridDataFactory.fillDefaults().applyTo(moduleNameLabel);
+
+ moduleName = new Text(composite, SWT.BORDER);
+ GridDataFactory.fillDefaults().grab(true, false).applyTo(moduleName);
+ moduleName.addModifyListener(modifyListener);
+
+ // Plugin
+ Label pluginNameLabel = new Label(composite, SWT.NONE);
+ pluginNameLabel.setText("Plugin");
+ GridDataFactory.fillDefaults().applyTo(pluginNameLabel);
+
+ Composite pluginNameComposite = new Composite(composite, SWT.NONE);
+ GridDataFactory.fillDefaults().grab(true, false).applyTo(pluginNameComposite);
+ GridLayoutFactory.fillDefaults().numColumns(2).applyTo(pluginNameComposite);
+
+ pluginName = new Text(pluginNameComposite, SWT.BORDER);
+ GridDataFactory.fillDefaults().grab(true, false).applyTo(pluginName);
+ pluginName.setText(initialPluginName);
+ pluginName.addModifyListener(modifyListener);
+
+ Button browsePlugins = new Button(pluginNameComposite, SWT.PUSH);
+ browsePlugins.setText("Browse...");
+ GridDataFactory.fillDefaults().applyTo(browsePlugins);
+ browsePlugins.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ browsePlugins();
+ }
+ });
+
+ // Focus
+ moduleName.setFocus();
+ parent.getDisplay().asyncExec(new Runnable() {
+ @Override
+ public void run() {
+ validate();
+ }
+ });
+
+ return composite;
+ }
+
+ private void browsePlugins() {
+ ArrayList<Entry> entries = new ArrayList<Entry>(bundles.size());
+ String currentPackageName = packageName.getText();
+ for(Bundle bundle : bundles.values())
+ entries.add(new Entry(bundle, CreateModuleAction.packageMatchLength(bundle, currentPackageName)));
+ PluginSelectionDialog dialog = new PluginSelectionDialog(getShell(), entries);
+ if(dialog.open() == Dialog.OK) {
+ Entry result = (Entry)dialog.getFirstResult();
+ if(result != null) {
+ pluginName.setText(result.bundle.getSymbolicName());
+ validate();
+ }
+ }
+ }
+
+ private void validate() {
+ boolean validPackageName = CreateModuleValidator.isValidPackageName(packageName.getText());
+ packageName.setBackground(validPackageName ? normalBackground : errorBackground);
+
+ boolean validModuleName = CreateModuleValidator.isValidModuleName(moduleName.getText());
+ if(validModuleName && validPackageName) {
+ String fullModuleName = packageName.getText() + "/" + moduleName.getText();
+ validModuleName = SCLOsgi.SOURCE_REPOSITORY.getModuleSource(fullModuleName, null) == null;
+ }
+ moduleName.setBackground(validModuleName ? normalBackground : errorBackground);
+
+ boolean validPluginName = bundles.containsKey(pluginName.getText());
+ pluginName.setBackground(validPluginName ? normalBackground : errorBackground);
+
+ getButton(IDialogConstants.OK_ID).setEnabled(validPackageName && validModuleName && validPackageName);
+ }
+
+ private ModifyListener modifyListener = new ModifyListener() {
+ @Override
+ public void modifyText(ModifyEvent e) {
+ validate();
+ }
+ };
+
+ public void setPackage(String initialPackageName) {
+ this.initialPackageName = initialPackageName;
+ this.initialPluginName = CreateModuleAction.findBestPlugin(bundles, initialPackageName);
+ }
+
+ @Override
+ protected void okPressed() {
+ try {
+ Bundle bundle = bundles.get(pluginName.getText());
+ if(bundle != null) {
+ CreateModuleAction.createModule(bundle, packageName.getText(), moduleName.getText());
+ parentBrowser.refresh();
+ OpenSCLModule.openModule(packageName.getText() + "/" + moduleName.getText());
+ }
+ } catch (IOException e) {
+ ErrorDialog.openError(getParentShell(), "Module creation failed", e.getMessage(),
+ new Status(Status.ERROR, "org.simantics.scl.ui", e.getMessage()));
+ }
+ super.okPressed();
+ }
+}
--- /dev/null
+package org.simantics.scl.ui.modulebrowser;
+
+public class CreateModuleValidator {
+ public static boolean isValidPackageName(String packageName) {
+ if(packageName.isEmpty())
+ return true;
+ for(String part : packageName.split("/", -1))
+ if(!isValidModuleName(part))
+ return false;
+ return true;
+ }
+
+ public static boolean isValidModuleName(String moduleName) {
+ if(moduleName.isEmpty())
+ return false;
+ {
+ char c = moduleName.charAt(0);
+ if(!Character.isLetter(c))
+ return false;
+ }
+ for(int i=1;i<moduleName.length();++i) {
+ char c = moduleName.charAt(i);
+ if(!Character.isLetter(c) && !Character.isDigit(c) && c != '_')
+ return false;
+ }
+ return true;
+ }
+}
--- /dev/null
+package org.simantics.scl.ui.modulebrowser;
+
+import java.util.Collection;
+import java.util.Comparator;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.dialogs.IDialogSettings;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.dialogs.FilteredItemsSelectionDialog;
+import org.eclipse.ui.dialogs.SearchPattern;
+import org.osgi.framework.Bundle;
+import org.simantics.scl.ui.Activator;
+
+public class PluginSelectionDialog extends FilteredItemsSelectionDialog {
+
+ private static final String PLUGIN_SELECTION_DIALOG = "PLUGIN_SELECTION_DIALOG";
+
+ public static class Entry {
+ Bundle bundle;
+ int packageMatchLength;
+ public Entry(Bundle bundle, int packageMatchLength) {
+ this.bundle = bundle;
+ this.packageMatchLength = packageMatchLength;
+ }
+ }
+ Collection<Entry> entries;
+
+ public PluginSelectionDialog(Shell shell, Collection<Entry> entries) {
+ super(shell, true);
+ this.entries = entries;
+ setListLabelProvider(new LabelProvider() {
+ @Override
+ public String getText(Object element) {
+ Entry entry = (Entry)element;
+ if(entry == null)
+ return "NULL";
+ return entry.bundle.getSymbolicName();
+ }
+ });
+ }
+
+ @Override
+ protected Control createExtendedContentArea(Composite parent) {
+ return null;
+ }
+
+ @Override
+ protected IDialogSettings getDialogSettings() {
+ IDialogSettings settings = org.simantics.scl.ui.Activator.getInstance()
+ .getDialogSettings().getSection(PLUGIN_SELECTION_DIALOG);
+ if (settings == null)
+ settings = Activator.getInstance()
+ .getDialogSettings().addNewSection(PLUGIN_SELECTION_DIALOG);
+ return settings;
+ }
+
+ @Override
+ protected IStatus validateItem(Object item) {
+ return Status.OK_STATUS;
+ }
+
+ @Override
+ protected ItemsFilter createFilter() {
+ return new ItemsFilter() {
+ {
+ String patternText = getPattern();
+ patternMatcher = new SearchPattern();
+ if(patternText != null && patternText.length() > 0)
+ patternMatcher.setPattern(patternText);
+ else
+ patternMatcher.setPattern("*");
+ }
+
+ @Override
+ public boolean matchItem(Object item) {
+ Entry entry = (Entry)item;
+ String name = entry.bundle.getSymbolicName();
+ if(getPattern().indexOf('/') > 0)
+ return matches(name);
+ else {
+ for(String part : name.split("/"))
+ if(matches(part))
+ return true;
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isConsistentItem(Object item) {
+ return true;
+ }
+
+ };
+ }
+
+ Comparator<Entry> comparator = new Comparator<Entry>() {
+ @Override
+ public int compare(Entry o1, Entry o2) {
+ if(o1.packageMatchLength != o2.packageMatchLength)
+ return Integer.compare(o2.packageMatchLength, o1.packageMatchLength);
+ return o1.bundle.getSymbolicName().compareTo(o2.bundle.getSymbolicName());
+ }
+ };
+
+ @SuppressWarnings("rawtypes")
+ @Override
+ protected Comparator getItemsComparator() {
+ return comparator;
+ }
+
+ @Override
+ protected void fillContentProvider(final AbstractContentProvider contentProvider,
+ final ItemsFilter itemsFilter, IProgressMonitor progressMonitor)
+ throws CoreException {
+ for(Entry entry : entries)
+ contentProvider.add(entry, itemsFilter);
+ if(progressMonitor != null)
+ progressMonitor.done();
+ }
+
+ @Override
+ public String getElementName(Object item) {
+ Entry entry = (Entry)item;
+ return entry.bundle.getSymbolicName();
+ }
+
+}
package org.simantics.scl.ui.modulebrowser;
import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.IMenuListener;
+import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Menu;
import org.eclipse.ui.part.ViewPart;
import org.simantics.scl.osgi.SCLOsgi;
import org.simantics.scl.ui.Activator;
public void createPartControl(Composite parent) {
this.content = new SCLModuleTree(parent, SWT.NONE, SCLOsgi.MODULE_REPOSITORY);
setPartName("SCL Modules");
+
+ // Opening modules
content.addDoubleClickListener(new IDoubleClickListener() {
@Override
public void doubleClick(DoubleClickEvent event) {
}
});
+ // Toolbar
IToolBarManager toolBarManager = getViewSite().getActionBars().getToolBarManager();
toolBarManager.add(new Action("Refresh modules",
Activator.imageDescriptorFromPlugin("org.simantics.scl.ui", "icons/arrow_refresh.png")) {
@Override
public void run() {
- SCLOsgi.MODULE_REPOSITORY.getSourceRepository().checkUpdates();
- content.recalculateInput();
+ refresh();
+ }
+ });
+
+ // Context menu
+ MenuManager menuMgr = new MenuManager();
+ menuMgr.setRemoveAllWhenShown(true);
+ menuMgr.addMenuListener(new IMenuListener() {
+ public void menuAboutToShow(IMenuManager manager) {
+ ModuleNameTreeEntry entry = (ModuleNameTreeEntry)content.getStructuredSelection().getFirstElement();
+ if(!entry.isModule)
+ manager.add(new Action("New Module...") {
+ @Override
+ public void run() {
+ CreateModuleDialog dialog = new CreateModuleDialog(content.getControl().getShell(), SCLModuleBrowser.this);
+ dialog.setPackage(entry.fullName);
+ dialog.open();
+ }
+ });
}
});
+ Menu menu = menuMgr.createContextMenu(content.getControl());
+ content.getControl().setMenu(menu);
+ getSite().registerContextMenu(menuMgr, content);
+ }
+
+ public void refresh() {
+ SCLOsgi.MODULE_REPOSITORY.getSourceRepository().checkUpdates();
+ content.recalculateInput();
}
@Override