Initial commit of Sigil contribution. (FELIX-1142)
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@793581 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/sigil/org.cauldron.sigil.ui/.classpath b/sigil/org.cauldron.sigil.ui/.classpath
new file mode 100644
index 0000000..6f93ad7
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/.classpath
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins">
+ <accessrules>
+ <accessrule kind="accessible" pattern="org/eclipse/pde/internal/ui/parts/*"/>
+ </accessrules>
+ </classpathentry>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/sigil/org.cauldron.sigil.ui/.project b/sigil/org.cauldron.sigil.ui/.project
new file mode 100644
index 0000000..e736268
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.cauldron.sigil.ui</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/sigil/org.cauldron.sigil.ui/META-INF/MANIFEST.MF b/sigil/org.cauldron.sigil.ui/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..4b4103a
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/META-INF/MANIFEST.MF
@@ -0,0 +1,34 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Ui Plug-in
+Bundle-SymbolicName: org.cauldron.sigil.ui;singleton:=true
+Bundle-Version: 0.8.0.qualifier
+Bundle-Activator: org.cauldron.sigil.ui.SigilUI
+Bundle-Vendor: Paremus Ltd
+Require-Bundle: org.cauldron.sigil.core,
+ org.cauldron.bld.core,
+ org.cauldron.sigil.utils,
+ org.cauldron.sigil.search,
+ org.eclipse.core.runtime,
+ org.eclipse.debug.ui,
+ org.eclipse.jdt.core,
+ org.eclipse.jdt.ui,
+ org.eclipse.jface.text,
+ org.eclipse.pde.ui,
+ org.eclipse.ui,
+ org.eclipse.ui.editors,
+ org.eclipse.ui.forms,
+ org.eclipse.ui.ide,
+ org.eclipse.help,
+ org.eclipse.zest.core;bundle-version="[1,2)",
+ org.eclipse.zest.layouts;bundle-version="[1,2)",
+ org.eclipse.ltk.core.refactoring
+Eclipse-LazyStart: true
+Export-Package: org.cauldron.sigil.actions,
+ org.cauldron.sigil.handlers,
+ org.cauldron.sigil.ui,
+ org.cauldron.sigil.ui.util,
+ org.cauldron.sigil.ui.wizard.project,
+ org.cauldron.sigil.ui.wizard.repository
+Bundle-Localization: plugin
+
diff --git a/sigil/org.cauldron.sigil.ui/build.properties b/sigil/org.cauldron.sigil.ui/build.properties
new file mode 100644
index 0000000..df5bef4
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/build.properties
@@ -0,0 +1,12 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ etc/,\
+ plugin.xml,\
+ icons/,\
+ resources/,\
+ toc.xml,\
+ html/,\
+ plugin.properties,\
+ schema/
diff --git a/sigil/org.cauldron.sigil.ui/plugin.properties b/sigil/org.cauldron.sigil.ui/plugin.properties
new file mode 100644
index 0000000..3e6d20d
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/plugin.properties
@@ -0,0 +1,5 @@
+repositoriesPrefPage=Repositories
+newtonRepoPrefs=Newton Repository
+librariesPrefsPage=OSGi Libraries
+commandConvertProject=Convert Project To Sigil Project
+commandRefreshClasspath=Refresh bundle classpath
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.ui/plugin.xml b/sigil/org.cauldron.sigil.ui/plugin.xml
new file mode 100644
index 0000000..d8b96a5
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/plugin.xml
@@ -0,0 +1,275 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<?eclipse version="3.2"?>
+<plugin>
+ <extension-point id="org.cauldron.sigil.ui.repositorywizard" name="Repository Wizard" schema="schema/org.cauldron.sigil.ui.repositorywizard.exsd"/>
+ <extension
+ point="org.eclipse.ui.startup">
+ <startup class="org.cauldron.sigil.startup.SigilStartup"/>
+ </extension>
+ <extension
+ point="org.eclipse.ui.newWizards">
+ <category
+ id="org.cauldron.sigil.newWizardCategory"
+ name="Sigil"/>
+ <wizard
+ category="org.cauldron.sigil.newWizardCategory"
+ class="org.cauldron.sigil.ui.wizard.project.SigilProjectWizard"
+ finalPerspective="org.cauldron.sigil.ui.perspective"
+ icon="etc/images/newton.png"
+ id="org.cauldron.sigil.editors.newProjectWizard"
+ name="Sigil Project"
+ project="true"/>
+ </extension>
+ <extension
+ point="org.eclipse.ui.preferencePages">
+ <page
+ class="org.cauldron.sigil.ui.preferences.SigilPreferencePage"
+ id="org.cauldron.sigil.ui.preferences.SigilPreferencePage"
+ name="Sigil"/>
+ <!--page
+ category="org.cauldron.sigil.ui.preferences.SigilPreferencePage"
+ class="org.cauldron.sigil.ui.preferences.LibraryPreferencePage"
+ id="org.cauldron.sigil.ui.libraryPreferences"
+ name="%librariesPrefsPage" >
+ </page-->
+ <page
+ category="org.cauldron.sigil.ui.preferences.SigilPreferencePage"
+ class="org.cauldron.sigil.ui.preferences.repository.RepositoriesPreferencePage"
+ id="org.cauldron.sigil.ui.preferences.repositoriesPreferencePage"
+ name="%repositoriesPrefPage">
+ </page>
+ <page
+ category="org.cauldron.sigil.ui.preferences.SigilPreferencePage"
+ class="org.cauldron.sigil.ui.preferences.ExcludedResourcesPrefsPage"
+ id="org.cauldron.sigil.ui.preferences.excludedResources"
+ name="Excluded Resources">
+ </page>
+ <page
+ category="org.cauldron.sigil.ui.preferences.SigilPreferencePage"
+ class="org.cauldron.sigil.ui.preferences.VersionsPreferencePage"
+ id="org.cauldron.sigil.ui.preferences.VersionsPreferencePage"
+ name="Version Handling">
+ </page>
+ <page
+ category="org.cauldron.sigil.ui.preferences.SigilPreferencePage"
+ class="org.cauldron.sigil.ui.preferences.installs.OSGiInstallsPreferencePage"
+ id="org.cauldron.sigil.ui.preferences.newtonInstalls"
+ name="Newton Installs">
+ </page>
+ </extension>
+ <extension
+ point="org.eclipse.ui.editors">
+ <editor
+ class="org.cauldron.sigil.ui.editors.project.SigilProjectEditorPart"
+ default="true"
+ filenames="sigil.properties"
+ icon="etc/images/newton.png"
+ id="org.cauldron.sigil.editors.SigilProjectEditor"
+ name="Sigil Project Editor"/>
+ </extension>
+ <extension
+ point="org.eclipse.jdt.core.classpathContainerInitializer">
+ <classpathContainerInitializer
+ class="org.cauldron.sigil.classpath.SigilClasspathContainerInitializer"
+ id="org.cauldron.sigil.core.classpathContainer"/>
+ </extension>
+ <extension
+ point="org.eclipse.jdt.ui.classpathContainerPage">
+ <classpathContainerPage
+ class="org.cauldron.sigil.classpath.SigilLibraryPage" id="org.cauldron.sigil.core.classpathContainer"
+ name="Sigil Library"/>
+ </extension>
+ <extension
+ point="org.eclipse.ui.perspectives">
+ <perspective
+ class="org.cauldron.sigil.ui.perspective.SigilPerspectiveFactory"
+ icon="etc/images/newton.png"
+ id="org.cauldron.sigil.ui.perspective"
+ name="Sigil">
+ </perspective>
+ </extension>
+ <extension
+ point="org.eclipse.jdt.ui.quickFixProcessors">
+ <quickFixProcessor
+ class="org.cauldron.sigil.ui.quickfix.ImportQuickFixProcessor"
+ id="org.cauldron.sigil.ui.quickFixProcessor1">
+ </quickFixProcessor>
+ </extension>
+ <extension
+ point="org.eclipse.ui.views">
+ <view
+ allowMultiple="false"
+ category="org.cauldron.sigil.ui.views"
+ class="org.cauldron.sigil.ui.views.resolution.BundleResolverView"
+ icon="icons/jar_web_obj.png"
+ id="org.cauldron.sigil.ui.bundleDependencyView"
+ name="Bundle Dependency View"
+ restorable="true">
+ </view>
+ <view
+ allowMultiple="false"
+ category="org.cauldron.sigil.ui.views"
+ class="org.cauldron.sigil.ui.views.RepositoryViewPart"
+ icon="icons/jars_obj.png"
+ id="org.cauldron.sigil.ui.repositoryBrowser"
+ name="Repository Browser"
+ restorable="true">
+ </view>
+ <category
+ id="org.cauldron.sigil.ui.views"
+ name="Sigil">
+ </category>
+ </extension>
+ <extension
+ point="org.cauldron.sigil.ui.repositorywizard">
+ <wizard
+ class="org.cauldron.sigil.ui.internal.repository.FileSystemRepositoryWizard"
+ repository="org.cauldron.sigil.core.file">
+ </wizard>
+ </extension>
+ <extension
+ point="org.eclipse.ui.commands">
+ <command
+ id="org.cauldron.sigil.ui.commands.renameComposite"
+ name="Rename Composite">
+ </command>
+ </extension>
+ <extension
+ point="org.eclipse.ltk.core.refactoring.refactoringContributions">
+ <contribution
+ class="org.cauldron.sigil.ui.refactor.RenameCompositeRefactoring"
+ id="org.cauldron.sigil.ui.rename.composite">
+ </contribution>
+ </extension>
+ <extension
+ point="org.eclipse.ui.propertyPages">
+ <page
+ class="org.cauldron.sigil.ui.preferences.project.ProjectPropertyPage"
+ id="org.cauldron.sigil.ui.projectpage"
+ name="Repositories">
+ <filter
+ name="projectNature"
+ value="org.cauldron.sigil.core.newtonnature"/>
+ <enabledWhen>
+ <adapt
+ type="org.eclipse.core.resources.IProject">
+ </adapt>
+ </enabledWhen>
+ </page>
+ </extension>
+ <extension
+ point="org.eclipse.ui.menus">
+ <menuContribution
+ locationURI="popup:org.eclipse.ui.popup.any">
+ <menu
+ icon="etc/images/newton.png"
+ label="Sigil">
+ <command
+ commandId="org.cauldron.sigil.ui.convertproject"
+ label="Convert Project"
+ style="push">
+ <visibleWhen
+ checkEnabled="true">
+ </visibleWhen>
+ </command>
+ </menu>
+ <separator
+ name="org.cauldron.sigil.ui.separator"
+ visible="true">
+ </separator>
+ <command
+ commandId="org.cauldron.sigil.ui.refreshclasspath"
+ icon="icons/refreshBundle.png"
+ label="Refresh bundle classpath"
+ style="push">
+ <visibleWhen
+ checkEnabled="true">
+ </visibleWhen>
+ </command>
+ </menuContribution>
+ </extension>
+ <extension
+ point="org.eclipse.ui.commands">
+ <command
+ id="org.cauldron.sigil.ui.convertproject"
+ name="%commandConvertProject">
+ </command>
+ <command
+ id="org.cauldron.sigil.ui.refreshclasspath"
+ name="%commandRefreshClasspath">
+ </command>
+ </extension>
+ <extension
+ point="org.eclipse.ui.commandImages">
+ <image
+ commandId="org.cauldron.sigil.ui.convertproject"
+ icon="etc/images/newton.png">
+ </image>
+ <image
+ commandId="org.cauldron.sigil.ui.refreshclasspath"
+ icon="etc/images/newton.png">
+ </image>
+ </extension>
+
+ <extension
+ point="org.eclipse.ui.handlers">
+ <handler
+ class="org.cauldron.sigil.handlers.project.ConvertProjectHandler"
+ commandId="org.cauldron.sigil.ui.convertproject">
+ <activeWhen>
+ <with
+ variable="selection">
+ <iterate
+ ifEmpty="false"
+ operator="and">
+ <instanceof
+ value="org.eclipse.core.resources.IProject">
+ </instanceof>
+ <test
+ property="org.cauldron.sigil.isSigilProject"
+ value="false">
+ </test>
+ </iterate>
+ </with>
+ </activeWhen>
+ </handler>
+ <handler
+ class="org.cauldron.sigil.handlers.project.RefreshSigilClasspathHandler"
+ commandId="org.cauldron.sigil.ui.refreshclasspath">
+ <activeWhen>
+ <with
+ variable="selection">
+ <iterate
+ ifEmpty="false"
+ operator="and">
+ <instanceof
+ value="org.eclipse.core.resources.IProject">
+ </instanceof>
+ <test
+ property="org.cauldron.sigil.isSigilProject"
+ value="true">
+ </test>
+ </iterate>
+ </with>
+ </activeWhen>
+ </handler>
+ </extension>
+</plugin>
diff --git a/sigil/org.cauldron.sigil.ui/resources/org/cauldron/sigil/ui/SigilUI.properties b/sigil/org.cauldron.sigil.ui/resources/org/cauldron/sigil/ui/SigilUI.properties
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/resources/org/cauldron/sigil/ui/SigilUI.properties
diff --git a/sigil/org.cauldron.sigil.ui/schema/org.cauldron.sigil.ui.repositorywizard.exsd b/sigil/org.cauldron.sigil.ui/schema/org.cauldron.sigil.ui.repositorywizard.exsd
new file mode 100644
index 0000000..e0ba4a4
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/schema/org.cauldron.sigil.ui.repositorywizard.exsd
@@ -0,0 +1,127 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.cauldron.sigil.ui" xmlns="http://www.w3.org/2001/XMLSchema">
+<annotation>
+ <appinfo>
+ <meta.schema plugin="org.cauldron.sigil.ui" id="org.cauldron.sigil.ui.repositorywizard" name="Repository Wizard"/>
+ </appinfo>
+ <documentation>
+ [Enter description of this extension point.]
+ </documentation>
+ </annotation>
+
+ <element name="extension">
+ <annotation>
+ <appinfo>
+ <meta.element />
+ </appinfo>
+ </annotation>
+ <complexType>
+ <sequence minOccurs="0" maxOccurs="unbounded">
+ <element ref="wizard"/>
+ </sequence>
+ <attribute name="point" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="id" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="name" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ <appinfo>
+ <meta.attribute translatable="true"/>
+ </appinfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="wizard">
+ <complexType>
+ <attribute name="class" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ <appinfo>
+ <meta.attribute kind="java" basedOn="org.cauldron.sigil.ui.wizard.repository.RepositoryWizard:"/>
+ </appinfo>
+ </annotation>
+ </attribute>
+ <attribute name="repository" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <annotation>
+ <appinfo>
+ <meta.section type="since"/>
+ </appinfo>
+ <documentation>
+ [Enter the first release in which this extension point appears.]
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appinfo>
+ <meta.section type="examples"/>
+ </appinfo>
+ <documentation>
+ [Enter extension point usage example here.]
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appinfo>
+ <meta.section type="apiinfo"/>
+ </appinfo>
+ <documentation>
+ [Enter API information here.]
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appinfo>
+ <meta.section type="implementation"/>
+ </appinfo>
+ <documentation>
+ [Enter information about supplied implementation of this extension point.]
+ </documentation>
+ </annotation>
+
+
+</schema>
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/actions/DisplayAction.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/actions/DisplayAction.java
new file mode 100644
index 0000000..852f972
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/actions/DisplayAction.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.actions;
+
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.actions.WorkspaceModifyOperation;
+
+public abstract class DisplayAction extends Action {
+
+ public DisplayAction() {
+ super();
+ }
+
+ public DisplayAction(String text) {
+ super(text);
+ }
+
+ public DisplayAction(String text, ImageDescriptor image) {
+ super(text, image);
+ }
+
+ public DisplayAction(String text, int style) {
+ super(text, style);
+ }
+
+ protected Display findDisplay() {
+ Display d = Display.getCurrent();
+
+ if ( d == null ) {
+ d = Display.getDefault();
+ }
+
+ return d;
+ }
+
+ protected void runInUI(final Shell shell, final WorkspaceModifyOperation op) {
+ }
+
+ protected void info(final Shell shell, final String msg) {
+ shell.getDisplay().asyncExec( new Runnable() {
+ public void run() {
+ MessageDialog.openInformation(shell, "Information", msg );
+ }
+ } );
+ }
+
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/actions/PruneProjectDependenciesAction.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/actions/PruneProjectDependenciesAction.java
new file mode 100644
index 0000000..86886bd
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/actions/PruneProjectDependenciesAction.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.actions;
+
+import java.util.Collection;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.model.util.JavaHelper;
+import org.cauldron.sigil.ui.SigilUI;
+import org.cauldron.sigil.ui.util.ResourceReviewDialog;
+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.core.runtime.jobs.Job;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.actions.WorkspaceModifyOperation;
+import org.eclipse.ui.progress.IProgressService;
+
+public class PruneProjectDependenciesAction extends DisplayAction {
+
+ private ISigilProjectModel project;
+
+ public PruneProjectDependenciesAction(ISigilProjectModel project) {
+ this.project = project;
+ }
+
+ @Override
+ public void run() {
+ final Shell shell = findDisplay().getActiveShell();
+
+ Job job = new Job("Resolving imports") {
+
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ Collection<IModelElement> unused = JavaHelper.findUnusedReferences(project, monitor);
+
+ if ( unused.isEmpty() ) {
+ info( shell, "No unused references found" );
+ }
+ else {
+ final ResourceReviewDialog<IModelElement> dialog = new ResourceReviewDialog<IModelElement>(shell, "Review Unused Imports", unused);
+
+ shell.getDisplay().asyncExec( new Runnable() {
+ public void run() {
+ if ( dialog.open() == Window.OK ) {
+ WorkspaceModifyOperation op = new WorkspaceModifyOperation() {
+ @Override
+ protected void execute(IProgressMonitor monitor) throws CoreException {
+ for ( IModelElement e : dialog.getResources() ) {
+ if ( !project.getBundle().getBundleInfo().removeChild(e) ) {
+ SigilCore.error( "Failed to remove " + e );
+ }
+ }
+
+ project.save(monitor);
+ }
+ };
+
+ SigilUI.runWorkspaceOperation(op, shell);
+ }
+ }
+ });
+ }
+
+ return Status.OK_STATUS;
+ }
+ };
+
+ job.schedule();
+
+ IProgressService p = PlatformUI.getWorkbench().getProgressService();
+ p.showInDialog(shell, job);
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/actions/RefreshRepositoryAction.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/actions/RefreshRepositoryAction.java
new file mode 100644
index 0000000..39d3abd
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/actions/RefreshRepositoryAction.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.actions;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.List;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.model.repository.IRepositoryModel;
+import org.cauldron.sigil.repository.IBundleRepository;
+import org.cauldron.sigil.ui.SigilUI;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.SubMonitor;
+import org.eclipse.ui.actions.WorkspaceModifyOperation;
+
+public class RefreshRepositoryAction extends DisplayAction {
+ private final IRepositoryModel[] model;
+
+ public RefreshRepositoryAction(IRepositoryModel... model) {
+ super( "Refresh repository");
+ this.model = model;
+ }
+
+ @Override
+ public void run() {
+ WorkspaceModifyOperation op = new WorkspaceModifyOperation() {
+
+ @Override
+ protected void execute(IProgressMonitor monitor)
+ throws CoreException, InvocationTargetException,
+ InterruptedException {
+ boolean changed = false;
+
+ for ( IBundleRepository b : SigilCore.getGlobalRepositoryManager().getRepositories() ) {
+ for ( IRepositoryModel m : model ) {
+ if ( b.getId().equals( m.getId() ) ) {
+ b.refresh();
+ changed = true;
+ }
+ }
+ }
+
+ if ( changed ) {
+ List<ISigilProjectModel> projects = SigilCore.getRoot().getProjects();
+ SubMonitor sub = SubMonitor.convert(monitor, projects.size() * 10);
+ for ( ISigilProjectModel p : projects ) {
+ p.resetClasspath(sub.newChild(10));
+ }
+ }
+ }
+ };
+
+ SigilUI.runWorkspaceOperation(op, null);
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/actions/ResolveProjectDependenciesAction.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/actions/ResolveProjectDependenciesAction.java
new file mode 100644
index 0000000..1f1ec1e
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/actions/ResolveProjectDependenciesAction.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.actions;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.model.util.JavaHelper;
+import org.cauldron.sigil.ui.SigilUI;
+import org.cauldron.sigil.ui.util.ResourceReviewDialog;
+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.core.runtime.jobs.Job;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.actions.WorkspaceModifyOperation;
+import org.eclipse.ui.progress.IProgressService;
+
+public class ResolveProjectDependenciesAction extends DisplayAction {
+
+ private ISigilProjectModel project;
+ private boolean review;
+
+ public ResolveProjectDependenciesAction(ISigilProjectModel project, boolean review) {
+ this.project = project;
+ this.review = review;
+ }
+
+ public void run() {
+ final Shell shell = findDisplay().getActiveShell();
+
+ Job job = new Job("Resolving dependencies" ) {
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ monitor.beginTask("", IProgressMonitor.UNKNOWN);
+
+ List<IPackageImport> imports = JavaHelper.findRequiredImports(project, monitor);
+
+ if ( imports.isEmpty() ) {
+ info( shell, "No new dependencies found" );
+ }
+ else {
+ Collections.sort(imports, new Comparator<IPackageImport>() {
+ public int compare(IPackageImport o1, IPackageImport o2) {
+ int i = o1.getPackageName().compareTo(o2.getPackageName());
+
+ // shouldn't get more than one import for same package
+ // but may as well sort if do...
+ if ( i == 0 ) {
+ i = o1.getVersions().getFloor().compareTo(o2.getVersions().getFloor() );
+ }
+
+ return i;
+ }
+ });
+
+ final ResourceReviewDialog<IPackageImport> dialog = new ResourceReviewDialog<IPackageImport>(shell, "Review New Dependencies", imports);
+ shell.getDisplay().asyncExec( new Runnable() {
+ public void run() {
+ if ( !review || dialog.open() == Window.OK ) {
+ WorkspaceModifyOperation op = new WorkspaceModifyOperation() {
+ @Override
+ protected void execute(IProgressMonitor monitor) throws CoreException {
+ for ( IPackageImport pi : dialog.getResources() ) {
+ project.getBundle().getBundleInfo().addImport(pi);
+ }
+
+ project.save(monitor);
+ }
+ };
+
+ SigilUI.runWorkspaceOperation(op, shell);
+ }
+ }
+ } );
+ }
+
+ return Status.OK_STATUS;
+ }
+ };
+
+ job.schedule();
+
+ IProgressService p = PlatformUI.getWorkbench().getProgressService();
+ p.showInDialog(shell, job);
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/classpath/SigilClassPathContainer.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/classpath/SigilClassPathContainer.java
new file mode 100644
index 0000000..69d8894
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/classpath/SigilClassPathContainer.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.classpath;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.job.ThreadProgressMonitor;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.jdt.core.IClasspathContainer;
+import org.eclipse.jdt.core.IClasspathEntry;
+
+/**
+ * @author dave
+ *
+ */
+public class SigilClassPathContainer implements IClasspathContainer {
+
+ private IClasspathEntry[] entries;
+ private ISigilProjectModel sigil;
+
+ public SigilClassPathContainer(ISigilProjectModel sigil) {
+ this.sigil = sigil;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jdt.core.IClasspathContainer#getClasspathEntries()
+ */
+ public IClasspathEntry[] getClasspathEntries() {
+ if ( entries == null ) {
+ buildClassPathEntries();
+ }
+
+ return entries;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jdt.core.IClasspathContainer#getDescription()
+ */
+ public String getDescription() {
+ return "Bundle Context Classpath";
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jdt.core.IClasspathContainer#getKind()
+ */
+ public int getKind() {
+ return K_SYSTEM;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jdt.core.IClasspathContainer#getPath()
+ */
+ public IPath getPath() {
+ return new Path( SigilCore.CLASSPATH_CONTAINER_PATH );
+ }
+
+ /**
+ * @return
+ * @throws CoreException
+ * @throws CoreException
+ */
+ private void buildClassPathEntries() {
+ try {
+ IProgressMonitor monitor = ThreadProgressMonitor.getProgressMonitor();
+ entries = sigil.findExternalClasspath(monitor).toArray( new IClasspathEntry[0] );
+ } catch (CoreException e) {
+ SigilCore.error( "Failed to build classpath entries", e);
+ }
+ finally {
+ if ( entries == null ) {
+ entries = new IClasspathEntry[] {};
+ }
+ }
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/classpath/SigilClasspathContainerInitializer.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/classpath/SigilClasspathContainerInitializer.java
new file mode 100644
index 0000000..f0229ee
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/classpath/SigilClasspathContainerInitializer.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.classpath;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.job.*;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jdt.core.ClasspathContainerInitializer;
+import org.eclipse.jdt.core.IClasspathContainer;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.JavaCore;
+
+public class SigilClasspathContainerInitializer extends ClasspathContainerInitializer {
+
+ public SigilClasspathContainerInitializer() {
+ // TODO Auto-generated constructor stub
+ }
+
+ @Override
+ public boolean canUpdateClasspathContainer(IPath containerPath,
+ IJavaProject project) {
+ return true;
+ }
+
+ @Override
+ public void requestClasspathContainerUpdate(IPath containerPath, IJavaProject project, IClasspathContainer containerSuggestion)
+ throws CoreException {
+ ISigilProjectModel sigil = SigilCore.create(project.getProject());
+
+ IClasspathContainer sigilContainer = new SigilClassPathContainer(sigil);
+
+ IJavaProject[] affectedProjects = new IJavaProject[] { project };
+
+ IClasspathContainer[] respectiveContainers = new IClasspathContainer[] { sigilContainer };
+
+ IProgressMonitor monitor = ThreadProgressMonitor.getProgressMonitor();
+
+ if ( monitor == null ) {
+ monitor = Job.getJobManager().createProgressGroup();
+ }
+
+ JavaCore.setClasspathContainer(containerPath, affectedProjects, respectiveContainers , monitor);
+ }
+
+ @Override
+ public void initialize(IPath containerPath, IJavaProject project)
+ throws CoreException {
+ ISigilProjectModel sigil = SigilCore.create(project.getProject());
+
+ IClasspathContainer sigilContainer = new SigilClassPathContainer(sigil);
+
+ IJavaProject[] affectedProjects = new IJavaProject[] { project };
+
+ IClasspathContainer[] respectiveContainers = new IClasspathContainer[] { sigilContainer };
+
+ IProgressMonitor monitor = Job.getJobManager().createProgressGroup();
+
+ JavaCore.setClasspathContainer(containerPath, affectedProjects, respectiveContainers , monitor);
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/classpath/SigilLibraryPage.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/classpath/SigilLibraryPage.java
new file mode 100644
index 0000000..cd73c8f
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/classpath/SigilLibraryPage.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.classpath;
+
+import org.cauldron.sigil.SigilCore;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.ui.wizards.IClasspathContainerPage;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+
+public class SigilLibraryPage extends WizardPage implements IClasspathContainerPage {
+
+ private IClasspathEntry classpathEntry;
+
+ public SigilLibraryPage() {
+ super( "Sigil" );
+ }
+
+ public void createControl(Composite parent) {
+ setTitle("Sigil Library");
+
+ Label label = new Label(parent, SWT.NONE);
+ label.setText("Press Finish to add the Sigil Library");
+ label.setFont(parent.getFont());
+
+ setControl(label);
+ }
+
+ public boolean finish() {
+ classpathEntry = JavaCore.newContainerEntry(new Path(SigilCore.CLASSPATH_CONTAINER_PATH));
+ return true;
+ }
+
+ public boolean isPageComplete() {
+ return true;
+ }
+
+ public IClasspathEntry getSelection() {
+ return classpathEntry;
+ }
+
+ public void setSelection(IClasspathEntry containerEntry) {
+ this.classpathEntry = containerEntry;
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/AbstractResourceCommandHandler.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/AbstractResourceCommandHandler.java
new file mode 100644
index 0000000..94184a9
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/AbstractResourceCommandHandler.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.handlers;
+
+import org.eclipse.core.commands.AbstractHandler;
+
+public abstract class AbstractResourceCommandHandler extends AbstractHandler {
+
+ protected abstract IResourceCommandHandler getResourceCommandHandler();
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/EditorResourceCommandHandler.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/EditorResourceCommandHandler.java
new file mode 100644
index 0000000..8bbcc6f
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/EditorResourceCommandHandler.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.handlers;
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.dialogs.ProgressMonitorDialog;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IFileEditorInput;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+public abstract class EditorResourceCommandHandler extends AbstractResourceCommandHandler {
+
+ public Object execute(ExecutionEvent event) throws ExecutionException {
+ final Shell shell = HandlerUtil.getActiveShell(event);
+ final IEditorPart editorPart = HandlerUtil.getActiveEditor(event);
+ if ( editorPart != null ) {
+ IEditorInput editorInput = editorPart.getEditorInput();
+
+ if(!(editorInput instanceof IFileEditorInput)) {
+ throw new ExecutionException("Editor input must be a file");
+ }
+ IFileEditorInput fileInput = (IFileEditorInput) editorInput;
+
+ try {
+ // Save the editor content (if dirty)
+ IRunnableWithProgress saveOperation = new IRunnableWithProgress() {
+ public void run(IProgressMonitor monitor) throws InvocationTargetException,
+ InterruptedException {
+ if(editorPart.isDirty()) {
+ if(MessageDialog.openQuestion(shell, "Save File", "The file contents must be saved before the command can be executed. Do you wish to save now?")) {
+ editorPart.doSave(monitor);
+ } else {
+ throw new InterruptedException();
+ }
+ }
+ }
+ };
+ new ProgressMonitorDialog(shell).run(false, true, saveOperation);
+
+ // Execute on the file
+ IFile file = fileInput.getFile();
+ getResourceCommandHandler().execute(new IResource[] { file }, event);
+ } catch (InvocationTargetException e) {
+ throw new ExecutionException("Error saving file", e.getTargetException());
+ } catch (InterruptedException e) {
+ // Exit the command silently
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/IResourceCommandHandler.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/IResourceCommandHandler.java
new file mode 100644
index 0000000..a1eb206
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/IResourceCommandHandler.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.handlers;
+
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.resources.IResource;
+
+public interface IResourceCommandHandler {
+
+ Object execute(IResource[] resources, ExecutionEvent event) throws ExecutionException;
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/SelectionResourceCommandHandler.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/SelectionResourceCommandHandler.java
new file mode 100644
index 0000000..b96cc73
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/SelectionResourceCommandHandler.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.handlers;
+
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+public abstract class SelectionResourceCommandHandler extends AbstractResourceCommandHandler {
+
+ public final Object execute(ExecutionEvent event) throws ExecutionException {
+ ISelection selection = HandlerUtil.getCurrentSelection(event);
+
+ Object[] objectArray = ((IStructuredSelection) selection).toArray();
+ IResource[] resourceArray = new IResource[objectArray.length];
+ System.arraycopy(objectArray, 0, resourceArray, 0, objectArray.length);
+
+ return getResourceCommandHandler().execute(resourceArray, event);
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/project/ConvertProjectCommandHandler.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/project/ConvertProjectCommandHandler.java
new file mode 100644
index 0000000..8e93c4e
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/project/ConvertProjectCommandHandler.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.handlers.project;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.handlers.IResourceCommandHandler;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.SigilUI;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.ui.actions.WorkspaceModifyOperation;
+
+public class ConvertProjectCommandHandler implements IResourceCommandHandler {
+
+ public Object execute(IResource[] resources, ExecutionEvent event)
+ throws ExecutionException {
+ for ( IResource r : resources ) {
+ final IProject project = (IProject) r;
+ if ( project != null ) {
+ WorkspaceModifyOperation op = new WorkspaceModifyOperation() {
+ @Override
+ protected void execute(IProgressMonitor monitor)
+ throws CoreException {
+ SigilCore.makeSigilProject(project, monitor);
+ IJavaProject java = JavaCore.create(project);
+ ISigilProjectModel sigil = SigilCore.create(project);
+ IClasspathEntry[] entries = java.getRawClasspath();
+ for (int i = 0; i < entries.length; i++) {
+ IClasspathEntry entry = entries[i];
+ if(entry.getEntryKind()==IClasspathEntry.CPE_SOURCE) {
+ String encodedClasspath = sigil.getJavaModel().encodeClasspathEntry( entry );
+ sigil.getBundle().addClasspathEntry(encodedClasspath);
+ }
+ }
+ sigil.save(monitor);
+ }
+ };
+ SigilUI.runWorkspaceOperation(op, null);
+ }
+ }
+
+ return null;
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/project/ConvertProjectHandler.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/project/ConvertProjectHandler.java
new file mode 100644
index 0000000..bf012f8
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/project/ConvertProjectHandler.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.handlers.project;
+
+import org.cauldron.sigil.handlers.IResourceCommandHandler;
+import org.cauldron.sigil.handlers.SelectionResourceCommandHandler;
+
+public class ConvertProjectHandler extends SelectionResourceCommandHandler {
+
+ @Override
+ protected IResourceCommandHandler getResourceCommandHandler() {
+ return new ConvertProjectCommandHandler();
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/project/RefreshSigilClasspathCommandHandler.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/project/RefreshSigilClasspathCommandHandler.java
new file mode 100644
index 0000000..0df3770
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/project/RefreshSigilClasspathCommandHandler.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.handlers.project;
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.handlers.IResourceCommandHandler;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.SigilUI;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.ui.actions.WorkspaceModifyOperation;
+
+public class RefreshSigilClasspathCommandHandler implements
+ IResourceCommandHandler {
+
+ public Object execute(IResource[] resources, ExecutionEvent event)
+ throws ExecutionException {
+ try {
+ for ( IResource res : resources ) {
+ IProject p = (IProject) res;
+ final ISigilProjectModel model = SigilCore.create(p);
+
+ WorkspaceModifyOperation op = new WorkspaceModifyOperation() {
+ @Override
+ protected void execute(IProgressMonitor monitor)
+ throws CoreException, InvocationTargetException,
+ InterruptedException {
+ model.resetClasspath(monitor);
+ }
+ };
+
+ SigilUI.runWorkspaceOperation(op, null);
+ }
+ } catch (CoreException e) {
+ SigilCore.error( "Failed to create sigil project for refresh action", e );
+ }
+ return null;
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/project/RefreshSigilClasspathHandler.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/project/RefreshSigilClasspathHandler.java
new file mode 100644
index 0000000..6dc20bb
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/handlers/project/RefreshSigilClasspathHandler.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.handlers.project;
+
+import org.cauldron.sigil.handlers.IResourceCommandHandler;
+import org.cauldron.sigil.handlers.SelectionResourceCommandHandler;
+
+public class RefreshSigilClasspathHandler extends SelectionResourceCommandHandler {
+
+ @Override
+ protected IResourceCommandHandler getResourceCommandHandler() {
+ return new RefreshSigilClasspathCommandHandler();
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/startup/SigilStartup.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/startup/SigilStartup.java
new file mode 100644
index 0000000..3f437bd
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/startup/SigilStartup.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.startup;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.job.ResolveProjectsJob;
+import org.cauldron.sigil.repository.IRepositoryChangeListener;
+import org.cauldron.sigil.repository.RepositoryChangeEvent;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.ui.IStartup;
+
+public class SigilStartup implements IStartup {
+
+ public void earlyStartup() {
+ // Create a task to run the resolver
+ final Runnable resolver = new Runnable() {
+ public void run() {
+ IWorkspace workspace = ResourcesPlugin.getWorkspace();
+ ResolveProjectsJob job = new ResolveProjectsJob(workspace);
+ job.setSystem(true);
+ job.schedule();
+ }
+ };
+
+ // Register a repository change listener to re-run the resolver when repository changes
+ SigilCore.getGlobalRepositoryManager().addRepositoryChangeListener(new IRepositoryChangeListener() {
+ public void repositoryChanged(RepositoryChangeEvent event) {
+ resolver.run();
+ }
+ });
+
+ // Run the resolver now
+ resolver.run();
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/SigilUI.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/SigilUI.java
new file mode 100644
index 0000000..a3c70d5
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/SigilUI.java
@@ -0,0 +1,181 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui;
+
+import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Locale;
+import java.util.ResourceBundle;
+
+import org.cauldron.sigil.SigilCore;
+import org.eclipse.jface.dialogs.ProgressMonitorDialog;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.resource.ImageRegistry;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.actions.WorkspaceModifyOperation;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+import org.osgi.framework.BundleContext;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class SigilUI extends AbstractUIPlugin {
+
+ // The plug-in ID
+ public static final String PLUGIN_ID = "org.cauldron.sigil.ui";
+
+ public static final String REPOSITORY_WIZARD_EXTENSION_POINT_ID = "org.cauldron.sigil.ui.repositorywizard";
+
+ public static final String PREF_NOPROMPT_INSTALL_COMPOSITE_WITH_ERRORS = "nopromptInstallCompositeError";
+ public static final String PREF_INSTALL_COMPOSITE_WITH_ERRORS_ANSWER = "answerInstallCompositeError";
+
+ public static final String ID_REPOSITORY_VIEW = "org.cauldron.sigil.ui.repositoryBrowser";
+ public static final String ID_DEPENDENCY_VIEW = "org.cauldron.sigil.ui.bundleDependencyView";
+ public static final String ID_INSTANCES_VIEW = "org.cauldron.sigil.runtime.newtonInstancesView";
+
+ // The shared instance
+ private static SigilUI plugin;
+
+ /**
+ * The constructor
+ */
+ public SigilUI() {
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
+ */
+ public void start(BundleContext context) throws Exception {
+ super.start(context);
+ SigilCore.getDefault();
+ plugin = this;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
+ */
+ public void stop(BundleContext context) throws Exception {
+ plugin = null;
+ super.stop(context);
+ }
+
+ /**
+ * Returns the shared instance
+ *
+ * @return the shared instance
+ */
+ public static SigilUI getDefault() {
+ return plugin;
+ }
+
+ public static ResourceBundle getResourceBundle() {
+ return ResourceBundle.getBundle( "resources." + SigilUI.class.getName(), Locale.getDefault(), SigilUI.class.getClassLoader() );
+ }
+
+ public static void runWorkspaceOperation(IRunnableWithProgress op, Shell shell) {
+ if ( shell == null ) {
+ shell = getActiveWorkbenchShell();
+ }
+ try {
+ new ProgressMonitorDialog(shell).run(true, true, op);
+ } catch (InvocationTargetException e) {
+ SigilCore.error( "Workspace operation failed", e);
+ } catch (InterruptedException e1) {
+ SigilCore.log( "Workspace operation interrupted");
+ }
+ }
+
+ public static void runWorkspaceOperationSync(IRunnableWithProgress op, Shell shell) throws Throwable {
+ if ( shell == null ) {
+ shell = getActiveWorkbenchShell();
+ }
+ try {
+ new ProgressMonitorDialog(shell).run(false, true, op);
+ } catch (InvocationTargetException e) {
+ throw e.getCause();
+ } catch (InterruptedException e1) {
+ SigilCore.log( "Workspace operation interrupted");
+ }
+ }
+ public static IWorkbenchWindow getActiveWorkbenchWindow() {
+ return getDefault().getWorkbench().getActiveWorkbenchWindow();
+ }
+
+ public static Shell getActiveWorkbenchShell() {
+ final Shell[] shell = new Shell[1];
+ runInUISync( new Runnable() {
+ public void run() {
+ shell[0] = getActiveDisplay().getActiveShell();
+ }
+ });
+ return shell[0];
+ }
+
+ public static Display getActiveDisplay() {
+ Display d = Display.getCurrent();
+
+ if ( d == null ) {
+ d = Display.getDefault();
+ }
+
+ return d;
+ }
+
+ public static void runInUI(Runnable runnable) {
+ getActiveDisplay().asyncExec(runnable);
+ }
+
+ public static void runInUISync(Runnable runnable) {
+ getActiveDisplay().syncExec(runnable);
+ }
+
+ public static Image cacheImage(String path, ClassLoader classLoader) {
+ ImageRegistry registry = SigilUI.getDefault().getImageRegistry();
+
+ Image image = registry.get( path );
+
+ if ( image == null ) {
+ image = loadImage( path, classLoader );
+ // XXX-FIXME-XXX add null image
+ if ( image != null ) {
+ registry.put( path, image);
+ }
+ }
+
+ return image;
+ }
+
+ private static Image loadImage(String resource, ClassLoader loader) {
+ InputStream in = loader.getResourceAsStream( resource );
+ if ( in != null ) {
+ ImageData data = new ImageData( in );
+ return new Image( SigilUI.getActiveDisplay(), data );
+ }
+ else {
+ return null;
+ }
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/completion/CompletionProposal.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/completion/CompletionProposal.java
new file mode 100644
index 0000000..b8e7d2e
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/completion/CompletionProposal.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.completion;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
+import org.eclipse.jface.text.contentassist.IContextInformation;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+
+class CompletionProposal implements ICompletionProposal {
+
+ private int fReplacementOffset;
+ private int fReplacementLength;
+ private int fCursorPosition;
+ private String fReplacementString;
+ private String fAdditionalProposalInfo;
+ private IContextInformation fContextInformation;
+ private String fDisplayString;
+ private Image fImage;
+
+ /**
+ * Creates a new completion proposal based on the provided information. The replacement string is
+ * considered being the display string too. All remaining fields are set to <code>null</code>.
+ *
+ * @param replacementString the actual string to be inserted into the document
+ * @param replacementOffset the offset of the text to be replaced
+ * @param replacementLength the length of the text to be replaced
+ * @param cursorPosition the position of the cursor following the insert relative to replacementOffset
+ */
+ public CompletionProposal(String replacementString, int replacementOffset, int replacementLength, int cursorPosition) {
+ this(replacementString, replacementOffset, replacementLength, cursorPosition, null, null, null, null);
+ }
+
+ /**
+ * Creates a new completion proposal. All fields are initialized based on the provided information.
+ *
+ * @param replacementString the actual string to be inserted into the document
+ * @param replacementOffset the offset of the text to be replaced
+ * @param replacementLength the length of the text to be replaced
+ * @param cursorPosition the position of the cursor following the insert relative to replacementOffset
+ * @param image the image to display for this proposal
+ * @param displayString the string to be displayed for the proposal
+ * @param contextInformation the context information associated with this proposal
+ * @param additionalProposalInfo the additional information associated with this proposal
+ */
+ public CompletionProposal(String replacementString, int replacementOffset, int replacementLength, int cursorPosition, Image image, String displayString, IContextInformation contextInformation, String additionalProposalInfo) {
+ fReplacementString= replacementString;
+ fReplacementOffset= replacementOffset;
+ fReplacementLength= replacementLength;
+ fCursorPosition= cursorPosition;
+ fImage= image;
+ fDisplayString= displayString;
+ fContextInformation= contextInformation;
+ fAdditionalProposalInfo= additionalProposalInfo;
+ }
+
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal#apply(org.eclipse.jface.text.IDocument)
+ */
+ public void apply( IDocument document ) {
+ try {
+ document.replace(fReplacementOffset, fReplacementLength, fReplacementString);
+ } catch (BadLocationException x) {
+ // ignore
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getAdditionalProposalInfo()
+ */
+ public String getAdditionalProposalInfo() {
+ return fAdditionalProposalInfo;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getContextInformation()
+ */
+ public IContextInformation getContextInformation() {
+ return fContextInformation;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getDisplayString()
+ */
+ public String getDisplayString() {
+ if (fDisplayString != null)
+ return fDisplayString;
+ return fReplacementString;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getImage()
+ */
+ public Image getImage() {
+ return fImage;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getSelection(org.eclipse.jface.text.IDocument)
+ */
+ public Point getSelection( IDocument document ) {
+ return new Point(fReplacementOffset + fCursorPosition, 0);
+ }
+
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/AbstractResourceSection.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/AbstractResourceSection.java
new file mode 100644
index 0000000..2ba92ca
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/AbstractResourceSection.java
@@ -0,0 +1,175 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import java.io.File;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.form.SigilPage;
+import org.cauldron.sigil.ui.form.SigilSection;
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceChangeEvent;
+import org.eclipse.core.resources.IResourceChangeListener;
+import org.eclipse.core.resources.IResourceVisitor;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.debug.core.DebugPlugin;
+import org.eclipse.jface.viewers.CheckStateChangedEvent;
+import org.eclipse.jface.viewers.CheckboxTreeViewer;
+import org.eclipse.jface.viewers.ICheckStateListener;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.ui.forms.IManagedForm;
+
+public abstract class AbstractResourceSection extends SigilSection implements IResourceChangeListener, ICheckStateListener {
+
+ protected static final Object[] EMPTY = new Object[]{};
+ protected Tree tree;
+ protected CheckboxTreeViewer viewer;
+ private IWorkspace workspace;
+
+ public AbstractResourceSection(SigilPage page, Composite parent,
+ ISigilProjectModel project) throws CoreException {
+ super(page, parent, project);
+ }
+
+ @Override
+ public void initialize(IManagedForm form) {
+ super.initialize(form);
+ viewer.setAllChecked(false);
+ viewer.setGrayedElements( EMPTY );
+ refreshSelections();
+ viewer.refresh();
+ }
+
+ @Override
+ public void refresh() {
+ refreshSelections();
+ super.refresh();
+ }
+
+ public void checkStateChanged(CheckStateChangedEvent event) {
+ handleStateChanged( (IResource) event.getElement(), event.getChecked(), true, true );
+ }
+
+ public void resourceChanged(IResourceChangeEvent event) {
+ if ( !getSection().getDisplay().isDisposed() ) {
+ getSection().getDisplay().asyncExec( new Runnable() {
+ public void run() {
+ if ( getSection().isVisible() ) {
+ viewer.refresh();
+ }
+ }
+ });
+ }
+ }
+
+ protected void startWorkspaceListener(IWorkspace workspace) {
+ this.workspace = workspace;
+ workspace.addResourceChangeListener(this);
+ }
+
+ @Override
+ public void dispose() {
+ workspace.removeResourceChangeListener(this);
+ }
+
+ protected abstract void refreshSelections();
+
+ protected abstract void syncResourceModel(IResource element, boolean checked);
+
+ protected IResource findResource(IPath path) {
+ IProject project2 = getProjectModel().getProject();
+ File f = project2.getLocation().append(path).toFile();
+
+ if ( f.exists() ) {
+ try {
+ if ( f.isFile() ) {
+ return project2.getFile(path);
+ }
+ else {
+ return project2.getFolder(path);
+ }
+ }
+ catch (IllegalArgumentException e) {
+ SigilCore.error( "Unknown path " + path );
+ return null;
+ }
+ }
+ else {
+ SigilCore.error( "Unknown file " + f );
+ return null;
+ }
+ }
+
+
+ protected void handleStateChanged(IResource element, boolean checked, boolean recurse,
+ boolean sync) {
+ if ( element instanceof IContainer ) {
+ setParentsGrayChecked(element, checked);
+ if ( recurse ) {
+ recursiveCheck( (IContainer) element, checked, sync );
+ }
+ }
+ else {
+ setParentsGrayChecked(element, checked);
+ }
+
+ if ( sync ) {
+ syncResourceModel( element, checked );
+ }
+ }
+
+ private void recursiveCheck(IContainer element, final boolean checked, final boolean sync) {
+ try {
+ element.accept( new IResourceVisitor() {
+ public boolean visit(IResource resource) throws CoreException {
+ viewer.setChecked(resource, checked);
+ if ( sync ) {
+ syncResourceModel(resource, checked);
+ }
+ return true;
+ }
+ } );
+ }
+ catch ( CoreException e ) {
+ DebugPlugin.log( e.getStatus() );
+ }
+ }
+
+ private void setParentsGrayChecked(IResource r, boolean checked) {
+ while ( r.getParent() != null ) {
+ r = r.getParent();
+ if ( (viewer.getGrayed(r) && viewer.getChecked(r)) == checked) {
+ break;
+ }
+ if ( viewer.getChecked(r) == checked ) {
+ viewer.setGrayed( r, !checked );
+ }
+ else {
+ viewer.setGrayChecked(r, checked);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/BundleDependencySection.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/BundleDependencySection.java
new file mode 100644
index 0000000..0750e0f
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/BundleDependencySection.java
@@ -0,0 +1,162 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.form.SigilPage;
+import org.cauldron.sigil.ui.form.SigilSection;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.viewers.IContentProvider;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.Section;
+import org.eclipse.ui.forms.widgets.TableWrapData;
+import org.eclipse.ui.forms.widgets.TableWrapLayout;
+
+public abstract class BundleDependencySection extends SigilSection {
+
+ private Set<? extends IModelElement> unresolvedElements;
+
+ protected ProjectTableViewer viewer;
+ private Button add;
+ private Button edit;
+ private Button remove;
+
+ public BundleDependencySection(SigilPage page, Composite parent, ISigilProjectModel project, Set<IModelElement> unresolvedElements) throws CoreException {
+ super(page, parent, project);
+ viewer.setUnresolvedElements(unresolvedElements);
+ }
+
+ public BundleDependencySection(SigilPage page, Composite parent, ISigilProjectModel project) throws CoreException {
+ this(page, parent, project, null);
+ }
+
+ protected abstract String getTitle();
+
+ protected abstract Label createLabel(Composite parent, FormToolkit toolkit);
+
+ protected abstract IContentProvider getContentProvider();
+
+ protected abstract void handleAdd();
+
+ protected abstract void handleEdit();
+
+ protected abstract void handleRemoved();
+
+ @Override
+ public void refresh() {
+ super.refresh();
+ viewer.refresh();
+ }
+
+ protected ISelection getSelection() {
+ return viewer.getSelection();
+ }
+
+ @Override
+ protected void createSection(Section section, FormToolkit toolkit) {
+ setTitle( getTitle() );
+
+ Composite body = createGridBody(2, false, toolkit);
+
+ Label label = createLabel(body, toolkit);
+
+ label.setLayoutData( new GridData(SWT.LEFT, SWT.CENTER, true, true, 2, 1 ) );
+
+ Table bundleTable = toolkit.createTable(body, SWT.MULTI | SWT.FULL_SELECTION | SWT.VIRTUAL | SWT.BORDER);
+ GridData data = new GridData( GridData.FILL_BOTH );
+ data.heightHint = 600;
+ bundleTable.setLayoutData( data );
+ bundleTable.setLinesVisible(false);
+ createButtons(body, toolkit);
+ createViewer( bundleTable );
+ }
+
+ protected void createButtons(Composite body, FormToolkit toolkit) {
+ Composite buttons = toolkit.createComposite(body);
+ TableWrapLayout layout = new TableWrapLayout();
+ layout.numColumns = 1;
+ layout.topMargin = 0;
+ layout.leftMargin = 0;
+ layout.rightMargin = 0;
+ layout.bottomMargin = 0;
+ buttons.setLayout( layout );
+ GridData data = new GridData();
+ data.verticalAlignment = SWT.TOP;
+ buttons.setLayoutData(data);
+
+ add = toolkit.createButton(buttons, "Add", SWT.NULL);
+ add.setLayoutData( new TableWrapData( TableWrapData.FILL ) );
+ add.addSelectionListener( new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ handleAdd();
+ }
+ } );
+
+ edit = toolkit.createButton(buttons, "Edit", SWT.NULL);
+ edit.setLayoutData( new TableWrapData( TableWrapData.FILL ) );
+ edit.addSelectionListener( new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ handleEdit();
+ }
+ } );
+
+ remove = toolkit.createButton(buttons, "Remove", SWT.NULL);
+ remove.setLayoutData( new TableWrapData( TableWrapData.FILL ) );
+ remove.addSelectionListener( new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ handleRemoved();
+ }
+ } );
+
+ setSelected(false);
+ }
+
+ protected void createViewer(Table bundleTable) {
+ viewer = new ProjectTableViewer(bundleTable);
+ viewer.setContentProvider(getContentProvider());
+ viewer.addSelectionChangedListener( new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent event) {
+ setSelected(!event.getSelection().isEmpty());
+ }
+ });
+ }
+
+ private void setSelected(boolean selected) {
+ edit.setEnabled(selected);
+ remove.setEnabled(selected);
+ }
+
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ClasspathSection.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ClasspathSection.java
new file mode 100644
index 0000000..311acca
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ClasspathSection.java
@@ -0,0 +1,240 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.model.util.JavaHelper;
+import org.cauldron.sigil.ui.form.SigilPage;
+import org.cauldron.sigil.ui.form.SigilSection;
+import org.cauldron.sigil.ui.util.BackgroundLoadingSelectionDialog;
+import org.cauldron.sigil.ui.util.DefaultTableProvider;
+import org.cauldron.sigil.ui.util.IFilter;
+import org.cauldron.sigil.ui.util.ModelLabelProvider;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jface.dialogs.ErrorDialog;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.ViewerComparator;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.Section;
+import org.eclipse.ui.forms.widgets.TableWrapData;
+import org.eclipse.ui.forms.widgets.TableWrapLayout;
+
+public class ClasspathSection extends SigilSection {
+
+ private ProjectTableViewer viewer;
+ private final Comparator<IClasspathEntry> CLASSPATH_COMPARATOR = new Comparator<IClasspathEntry>() {
+ public int compare(IClasspathEntry o1, IClasspathEntry o2) {
+ return compareClasspaths(o1, o2);
+ }
+ };
+
+ public ClasspathSection(SigilPage page, Composite parent, ISigilProjectModel project) throws CoreException {
+ super( page, parent, project );
+ }
+
+ @Override
+ protected void createSection(Section section, FormToolkit toolkit) {
+ setTitle( "Classpath");
+
+ Composite body = createGridBody(2, false, toolkit);
+
+ Label label = toolkit.createLabel( body, "Specify the internal classpath of this bundle." );
+ label.setLayoutData( new GridData(SWT.LEFT, SWT.CENTER, true, true, 2, 1 ) );
+
+ Table bundleTable = toolkit.createTable(body, SWT.MULTI | SWT.FULL_SELECTION | SWT.VIRTUAL | SWT.BORDER);
+ GridData tableLayoutData = new GridData(SWT.FILL, SWT.FILL, true, true);
+ tableLayoutData.heightHint = 150;
+ bundleTable.setLayoutData(tableLayoutData);
+
+ createButtons(body, toolkit);
+ createViewer( bundleTable );
+ }
+
+ private void createButtons(Composite body, FormToolkit toolkit) {
+ Composite buttons = toolkit.createComposite(body);
+ TableWrapLayout layout = new TableWrapLayout();
+ layout.numColumns = 1;
+ layout.topMargin = 0;
+ layout.leftMargin = 0;
+ layout.rightMargin = 0;
+ layout.bottomMargin = 0;
+ buttons.setLayout( layout );
+
+ Button add = toolkit.createButton(buttons, "Add", SWT.NULL);
+ add.setLayoutData( new TableWrapData( TableWrapData.FILL ) );
+ add.addSelectionListener( new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ handleAdd();
+ }
+ } );
+
+ Button remove = toolkit.createButton(buttons, "Remove", SWT.NULL);
+ remove.setLayoutData( new TableWrapData( TableWrapData.FILL ) );
+ remove.addSelectionListener( new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ handleRemoved();
+ }
+ } );
+ }
+
+ private void createViewer(Table bundleTable) {
+ viewer = new ProjectTableViewer(bundleTable);
+ viewer.setContentProvider( new DefaultTableProvider() {
+ public Object[] getElements(Object inputElement) {
+ ArrayList<IClasspathEntry> cp = new ArrayList<IClasspathEntry>();
+ for (IClasspathEntry cpe : JavaHelper.findClasspathEntries(getBundle())) {
+ cp.add(cpe);
+ }
+
+ Collections.sort(cp, new Comparator<IClasspathEntry>() {
+ public int compare(IClasspathEntry o1, IClasspathEntry o2) {
+ return o1.toString().compareTo(o2.toString());
+ }
+ });
+ return cp.toArray();
+ }
+ });
+ viewer.setComparator(new ViewerComparator() {
+ @Override
+ public int category(Object element) {
+ return index((IClasspathEntry) element);
+ }
+ });
+ }
+
+ protected ISigilBundle getBundle() {
+ return getProjectModel().getBundle();
+ }
+
+ private void handleAdd() {
+ try {
+ BackgroundLoadingSelectionDialog<IClasspathEntry> dialog = new BackgroundLoadingSelectionDialog<IClasspathEntry>(getSection().getShell(), "Classpath Entry:", true);
+
+ dialog.setDescriptor(new IElementDescriptor<IClasspathEntry>() {
+ public String getName(IClasspathEntry element) {
+ return element.getPath().toString();
+ }
+
+ public String getLabel(IClasspathEntry element) {
+ return getName(element);
+ }
+ });
+
+ dialog.setLabelProvider(new ModelLabelProvider());
+
+ dialog.setFilter(new IFilter<IClasspathEntry>() {
+ public boolean select(IClasspathEntry cp) {
+ switch ( cp.getEntryKind() ) {
+ case IClasspathEntry.CPE_LIBRARY:
+ case IClasspathEntry.CPE_VARIABLE:
+ case IClasspathEntry.CPE_SOURCE:
+ return !getBundle().getClasspathEntrys().contains(encode(cp));
+ default:
+ return false;
+ }
+ }
+ });
+
+ dialog.setComparator(CLASSPATH_COMPARATOR);
+
+ IClasspathEntry[] classpath = getProjectModel().getJavaModel().getRawClasspath();
+ dialog.addElements(Arrays.asList(classpath));
+ if ( dialog.open() == Window.OK ) {
+ List<IClasspathEntry> selectedElements = dialog.getSelectedElements();
+
+ Object[] added = selectedElements.toArray();
+ for (IClasspathEntry entry : selectedElements) {
+ getBundle().addClasspathEntry(encode(entry));
+ }
+ viewer.add(added);
+ viewer.refresh();
+ markDirty();
+ }
+ } catch (JavaModelException e) {
+ ErrorDialog.openError(getSection().getShell(), "Error", null, e.getStatus());
+ }
+ }
+
+ private int compareClasspaths(IClasspathEntry o1, IClasspathEntry o2) {
+ if ( o1.getEntryKind() == o2.getEntryKind() ) {
+ ModelLabelProvider mlp = viewer.getLabelProvider();
+ return mlp.getText(o1).compareTo(mlp.getText(o2));
+ }
+ else {
+ int i1 = index(o1);
+ int i2 = index(o2);
+
+ if ( i1 < i2 ) {
+ return -1;
+ }
+ else {
+ return 1;
+ }
+ }
+ }
+
+ private static int index(IClasspathEntry o1) {
+ switch ( o1.getEntryKind() ) {
+ case IClasspathEntry.CPE_SOURCE: return 0;
+ case IClasspathEntry.CPE_PROJECT: return 1;
+ case IClasspathEntry.CPE_LIBRARY: return 2;
+ case IClasspathEntry.CPE_VARIABLE: return 3;
+ case IClasspathEntry.CPE_CONTAINER: return 4;
+ default: throw new IllegalStateException( "Unknown classpath entry type " + o1);
+ }
+ }
+
+ private String encode(IClasspathEntry cp) {
+ return getProjectModel().getJavaModel().encodeClasspathEntry(cp).trim();
+ }
+
+ @SuppressWarnings("unchecked")
+ private void handleRemoved() {
+ IStructuredSelection selection = (IStructuredSelection) viewer.getSelection();
+
+ if ( !selection.isEmpty() ) {
+ for ( Iterator<IClasspathEntry> i = selection.iterator(); i.hasNext(); ) {
+ getBundle().removeClasspathEntry(getProjectModel().getJavaModel().encodeClasspathEntry(i.next()).trim());
+ }
+ viewer.remove(selection.toArray());
+ markDirty();
+ }
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ContainerTreeProvider.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ContainerTreeProvider.java
new file mode 100644
index 0000000..5d091fb
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ContainerTreeProvider.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import org.cauldron.sigil.ui.util.DefaultTreeContentProvider;
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.debug.core.DebugPlugin;
+
+public class ContainerTreeProvider extends DefaultTreeContentProvider {
+
+ private static final Object[] EMPTY = new Object[] {};
+
+ public Object[] getChildren(Object parentElement) {
+ if ( parentElement instanceof IContainer ) {
+ IContainer f = (IContainer) parentElement;
+ try {
+ return f.members();
+ } catch (CoreException e) {
+ DebugPlugin.log( e.getStatus() );
+ }
+ }
+ return EMPTY;
+ }
+
+ public Object getParent(Object element) {
+ IResource r = (IResource) element;
+ return r.getParent();
+ }
+
+ public boolean hasChildren(Object element) {
+ if ( element instanceof IContainer ) {
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+
+ public Object[] getElements(Object inputElement) {
+ IContainer container = (IContainer) inputElement;
+ return getChildren(container);
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ContentSummarySection.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ContentSummarySection.java
new file mode 100644
index 0000000..7d1fbcb
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ContentSummarySection.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.form.SigilPage;
+import org.cauldron.sigil.ui.form.SigilSection;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.forms.events.HyperlinkEvent;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.Hyperlink;
+import org.eclipse.ui.forms.widgets.Section;
+
+public class ContentSummarySection extends SigilSection {
+
+ public ContentSummarySection(SigilPage page, Composite parent, ISigilProjectModel project) throws CoreException {
+ super( page, parent, project );
+ }
+
+ @Override
+ protected void createSection(Section section, FormToolkit toolkit) {
+ setTitle( "Project Content");
+
+ Composite body = createTableWrapBody(2, toolkit);
+ Hyperlink link = toolkit.createHyperlink( body, "Contents:", SWT.NONE );
+ link.setHref( ContentsForm.PAGE_ID );
+ link.addHyperlinkListener(this);
+ toolkit.createLabel( body, "Manage the content that this bundle provides." );
+
+ link = toolkit.createHyperlink( body, "Dependencies:", SWT.NONE );
+ link.setHref( DependenciesForm.PAGE_ID );
+ link.addHyperlinkListener(this);
+ toolkit.createLabel( body, "Manage the dependencies that this bundle needs to run." );
+
+ link = toolkit.createHyperlink( body, "Exports:", SWT.NONE );
+ link.setHref( ExportsForm.PAGE_ID );
+ link.addHyperlinkListener(this);
+ toolkit.createLabel( body, "Manage the resources that this bundle exports." );
+ }
+
+ @Override
+ public void linkActivated(HyperlinkEvent e) {
+ getPage().getEditor().setActivePage( (String) e.getHref() );
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ContentsForm.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ContentsForm.java
new file mode 100644
index 0000000..62d58a8
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ContentsForm.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.form.SigilPage;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.forms.IManagedForm;
+import org.eclipse.ui.forms.editor.FormEditor;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.ScrolledForm;
+import org.eclipse.ui.forms.widgets.TableWrapData;
+import org.eclipse.ui.forms.widgets.TableWrapLayout;
+
+public class ContentsForm extends SigilPage {
+
+ public static final String PAGE_ID = "contents";
+
+ private ISigilProjectModel project;
+
+ public ContentsForm(FormEditor editor, ISigilProjectModel project) {
+ super(editor, PAGE_ID, "Contents");
+ this.project = project;
+ }
+
+ @Override
+ protected void createFormContent(IManagedForm managedForm) {
+ FormToolkit toolkit = managedForm.getToolkit();
+
+ ScrolledForm form = managedForm.getForm();
+ form.setText( "Contents" );
+
+ Composite body = form.getBody();
+ TableWrapLayout layout = new TableWrapLayout();
+ layout.bottomMargin = 10;
+ layout.topMargin = 5;
+ layout.leftMargin = 10;
+ layout.rightMargin = 10;
+ layout.numColumns = 2;
+ layout.horizontalSpacing = 10;
+ body.setLayout(layout);
+ body.setLayoutData(new TableWrapData(TableWrapData.FILL));
+
+ Composite top = toolkit.createComposite(body);
+ layout = new TableWrapLayout();
+ layout.verticalSpacing = 20;
+ top.setLayout(layout);
+ TableWrapData data = new TableWrapData(TableWrapData.FILL_GRAB);
+ data.colspan = 2;
+ top.setLayoutData(data);
+
+ Composite left = toolkit.createComposite(body);
+ layout = new TableWrapLayout();
+ layout.verticalSpacing = 20;
+ left.setLayout(layout);
+ left.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB));
+
+ Composite right = toolkit.createComposite(body);
+ layout = new TableWrapLayout();
+ layout.verticalSpacing = 20;
+ right.setLayout(layout);
+ right.setLayoutData( new TableWrapData( TableWrapData.FILL_GRAB) );
+
+ try {
+ ClasspathSection classpath = new ClasspathSection( this, top, project );
+ managedForm.addPart( classpath );
+
+ ResourceBuildSection runtimeBuild = new ResourceBuildSection( this, left, project );
+ managedForm.addPart( runtimeBuild );
+
+ DownloadSection download = new DownloadSection( this, right, project );
+ managedForm.addPart( download );
+ }
+ catch (CoreException e) {
+ SigilCore.error( "Failed to create contents form", e);
+ }
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/DependenciesForm.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/DependenciesForm.java
new file mode 100644
index 0000000..1c28ba5
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/DependenciesForm.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import java.util.Set;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.form.SigilPage;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.forms.IManagedForm;
+import org.eclipse.ui.forms.editor.FormEditor;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.ScrolledForm;
+import org.eclipse.ui.forms.widgets.TableWrapData;
+import org.eclipse.ui.forms.widgets.TableWrapLayout;
+
+public class DependenciesForm extends SigilPage {
+
+ public static final String PAGE_ID = "dependencies";
+
+ private final ISigilProjectModel project;
+ private final Set<IModelElement> unresolvedElements;
+
+ private ImportPackagesSection importPackages;
+ private RequiresBundleSection requiresSection;
+
+
+ public DependenciesForm(FormEditor editor, ISigilProjectModel project, Set<IModelElement> unresolved) {
+ super(editor, PAGE_ID, "Dependencies");
+ this.project = project;
+ this.unresolvedElements = unresolved;
+ }
+
+ @Override
+ public boolean canLeaveThePage() {
+ return !isDirty();
+ }
+
+ @Override
+ protected void createFormContent(IManagedForm managedForm) {
+ FormToolkit toolkit = managedForm.getToolkit();
+
+ ScrolledForm form = managedForm.getForm();
+ form.setText( "Dependencies" );
+
+ Composite body = form.getBody();
+ TableWrapLayout layout = new TableWrapLayout();
+ layout.bottomMargin = 10;
+ layout.topMargin = 5;
+ layout.leftMargin = 10;
+ layout.rightMargin = 10;
+ layout.numColumns = 2;
+ layout.horizontalSpacing = 10;
+ body.setLayout(layout);
+ body.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB));
+
+ Composite left = toolkit.createComposite(body);
+ layout = new TableWrapLayout();
+ layout.verticalSpacing = 20;
+ left.setLayout(layout);
+ TableWrapData layoutData = new TableWrapData(TableWrapData.FILL_GRAB);
+ layoutData.rowspan = 2;
+ left.setLayoutData(layoutData);
+
+ Composite right = toolkit.createComposite(body);
+ layout = new TableWrapLayout();
+ layout.verticalSpacing = 20;
+ right.setLayout(layout);
+ right.setLayoutData(new TableWrapData( TableWrapData.FILL_GRAB) );
+
+ Composite bottom = toolkit.createComposite(body);
+ layout = new TableWrapLayout();
+ layout.verticalSpacing = 20;
+ bottom.setLayout(layout);
+ bottom.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB));
+
+ try {
+ importPackages = new ImportPackagesSection( this, left, project, unresolvedElements );
+ managedForm.addPart( importPackages );
+
+ requiresSection = new RequiresBundleSection(this, right, project, unresolvedElements );
+ managedForm.addPart(requiresSection);
+
+ DependencyManagementSection depMgmt = new DependencyManagementSection(this, bottom, project);
+ managedForm.addPart(depMgmt);
+ }
+ catch (CoreException e) {
+ SigilCore.error( "Failed to create dependencies form", e);
+ }
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/DependencyManagementSection.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/DependencyManagementSection.java
new file mode 100644
index 0000000..aadf990
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/DependencyManagementSection.java
@@ -0,0 +1,196 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.common.VersionRangeBoundingRule;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.SigilUI;
+import org.cauldron.sigil.ui.form.SigilPage;
+import org.cauldron.sigil.ui.form.SigilSection;
+import org.cauldron.sigil.ui.util.AccumulatorAdapter;
+import org.cauldron.sigil.ui.util.ExportedPackageFinder;
+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.core.runtime.jobs.Job;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IImportDeclaration;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IPackageFragment;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.forms.AbstractFormPart;
+import org.eclipse.ui.forms.IFormPart;
+import org.eclipse.ui.forms.events.HyperlinkAdapter;
+import org.eclipse.ui.forms.events.HyperlinkEvent;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.Hyperlink;
+import org.eclipse.ui.forms.widgets.Section;
+
+public class DependencyManagementSection extends SigilSection {
+
+ private Hyperlink hypConvertRBtoIP;
+
+ public DependencyManagementSection(SigilPage page, Composite parent,
+ ISigilProjectModel project) throws CoreException {
+ super(page, parent, project);
+ }
+
+ protected void createSection(Section section, FormToolkit toolkit) throws CoreException {
+ setTitle("Dependency Management");
+
+ Composite body = createGridBody(1, false, toolkit);
+
+ hypConvertRBtoIP = toolkit.createHyperlink(body, "Convert Required Bundles to Imported Packages", SWT.NONE);
+ hypConvertRBtoIP.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+
+ hypConvertRBtoIP.addHyperlinkListener(new HyperlinkAdapter() {
+ public void linkActivated(HyperlinkEvent e) {
+ run();
+ }
+ });
+ }
+
+ protected void run() {
+ final Map<String, IPackageExport> exports = new HashMap<String, IPackageExport>();
+ final Set<String> imports = new HashSet<String>();
+
+ // Find all exports
+ final ExportedPackageFinder exportFinder = new ExportedPackageFinder(getProjectModel(), new AccumulatorAdapter<IPackageExport>() {
+ public void addElements(Collection<? extends IPackageExport> elements) {
+ for (IPackageExport export : elements) {
+ exports.put(export.getPackageName(), export);
+ }
+ }
+ });
+ Job findExportsJob = new Job("Find exports") {
+ protected IStatus run(IProgressMonitor monitor) {
+ return exportFinder.run(monitor);
+ }
+ };
+ findExportsJob.setUser(true);
+ findExportsJob.schedule();
+
+ // Find imports from Java source
+ Job findImportsJob = new Job("Find imports") {
+ protected IStatus run(IProgressMonitor monitor) {
+ IJavaProject javaProject = getProjectModel().getJavaModel();
+ try {
+ IPackageFragment[] packages = javaProject.getPackageFragments();
+ for (IPackageFragment pkg : packages) {
+ ICompilationUnit[] compilationUnits = pkg.getCompilationUnits();
+ for (ICompilationUnit compilationUnit : compilationUnits) {
+ IImportDeclaration[] importDecls = compilationUnit.getImports();
+ for (IImportDeclaration importDecl : importDecls) {
+ imports.add(getPackageName(importDecl));
+ }
+ }
+ }
+ return Status.OK_STATUS;
+ } catch (JavaModelException e) {
+ return new Status(IStatus.ERROR, SigilUI.PLUGIN_ID, 0, "Error finding imports", e);
+ }
+ }
+ };
+ findImportsJob.setUser(true);
+ findImportsJob.schedule();
+
+ // Wait for both jobs to complete
+ try {
+ findImportsJob.join();
+ findExportsJob.join();
+ } catch (InterruptedException e) {
+ // Aborted, just do nothing
+ return;
+ }
+
+ // Get the version rules
+ IPreferenceStore prefStore = SigilCore.getDefault().getPreferenceStore();
+ VersionRangeBoundingRule lowerBoundRule = VersionRangeBoundingRule.valueOf(prefStore.getString(SigilCore.DEFAULT_VERSION_LOWER_BOUND));
+ VersionRangeBoundingRule upperBoundRule = VersionRangeBoundingRule.valueOf(prefStore.getString(SigilCore.DEFAULT_VERSION_UPPER_BOUND));
+
+ // Get the existing imports for the bundle
+ IBundleModelElement bundleInfo = getProjectModel().getBundle().getBundleInfo();
+ Set<IPackageImport> existingImports = bundleInfo.getImports();
+ Map<String, IPackageImport> existingImportsMap = new HashMap<String, IPackageImport>();
+ for (IPackageImport existingImport : existingImports) {
+ existingImportsMap.put(existingImport.getPackageName(), existingImport);
+ }
+
+ // Add imports to the bundle
+ ModelElementFactory elementFactory = ModelElementFactory.getInstance();
+ int count = 0;
+ for (String pkgImport : imports) {
+ IPackageExport export = exports.get(pkgImport);
+ if(export != null && !existingImportsMap.containsKey(pkgImport)) {
+ VersionRange versionRange = VersionRange.newInstance(export.getVersion(), lowerBoundRule, upperBoundRule);
+ IPackageImport newImport = elementFactory.newModelElement(IPackageImport.class);
+ newImport.setPackageName(pkgImport);
+ newImport.setVersions(versionRange);
+ newImport.setOptional(false);
+
+ bundleInfo.addImport(newImport);
+ count++;
+ }
+ }
+
+ // Remove required bundles
+ Set<IRequiredBundle> requiredBundles = bundleInfo.getRequiredBundles();
+ int requiredBundlesSize = requiredBundles.size();
+ for (IRequiredBundle requiredBundle : requiredBundles) {
+ bundleInfo.removeRequiredBundle(requiredBundle);
+ }
+
+ // Update the editor
+ if(count + requiredBundlesSize > 0) {
+ IFormPart[] parts = getPage().getManagedForm().getParts();
+ for (IFormPart formPart : parts) {
+ formPart.refresh();
+ ((AbstractFormPart) formPart).markDirty();
+ }
+ }
+
+ MessageDialog.openInformation(getManagedForm().getForm().getShell(), "Dependency Management", "Removed " + requiredBundlesSize + " required bundle(s) and added " + count + " imported package(s).");
+ }
+
+ private static String getPackageName(IImportDeclaration decl) {
+ String name = decl.getElementName();
+ int lastDot = name.lastIndexOf('.');
+ return name.substring(0, lastDot);
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/DownloadSection.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/DownloadSection.java
new file mode 100644
index 0000000..ad2e705
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/DownloadSection.java
@@ -0,0 +1,143 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+
+
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.ModelElementFactoryException;
+import org.cauldron.sigil.model.eclipse.IDownloadJar;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.form.SigilPage;
+import org.cauldron.sigil.ui.util.ModelLabelProvider;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.jface.viewers.CheckboxTreeViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.Section;
+import org.eclipse.ui.forms.widgets.TableWrapData;
+
+/**
+ * @author dave
+ *
+ */
+public class DownloadSection extends AbstractResourceSection {
+
+ /**
+ * @param page
+ * @param parent
+ * @param project
+ * @throws CoreException
+ */
+
+ private IDownloadJar dl;
+
+ public DownloadSection(SigilPage page, Composite parent, ISigilProjectModel project) throws CoreException {
+ super( page, parent, project );
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.ui.editors.project.SigilSection#createSection(org.eclipse.ui.forms.widgets.Section, org.eclipse.ui.forms.widgets.FormToolkit)
+ */
+ @Override
+ protected void createSection(Section section, FormToolkit toolkit) throws CoreException {
+ setTitle( "Codebase" );
+
+ Composite body = createTableWrapBody(1, toolkit);
+
+ toolkit.createLabel( body, "Specify which resources are included as part of this bundles remote codebase." );
+
+ tree = toolkit.createTree( body, SWT.CHECK | SWT.BORDER );
+
+ TableWrapData data = new TableWrapData( TableWrapData.FILL_GRAB);
+ data.heightHint = 200;
+ tree.setLayoutData( data );
+
+ viewer = new CheckboxTreeViewer( tree );
+ IFolder base = ResourcesPlugin.getWorkspace().getRoot().getFolder(getProjectModel().getJavaModel().getOutputLocation());
+ viewer.setContentProvider( new ContainerTreeProvider() );
+ viewer.setLabelProvider( new ModelLabelProvider() );
+ viewer.addCheckStateListener( this );
+ viewer.setInput( base );
+
+ dl = getProjectModel().getBundle().getDownloadJar();
+
+ startWorkspaceListener(base.getWorkspace());
+ }
+
+ @Override
+ public void refresh() {
+ dl = getProjectModel().getBundle().getDownloadJar();
+ super.refresh();
+ }
+
+ @Override
+ public void commit(boolean onSave) {
+ getProjectModel().getBundle().setDownloadJar(dl);
+ super.commit(onSave);
+ }
+
+ @Override
+ protected void refreshSelections() {
+ // zero the state
+ if ( dl != null ) {
+ for ( IPath path : dl.getEntrys() ) {
+ IResource r = findResource( path );
+ if ( r != null ) {
+ viewer.expandToLevel(r, 0);
+ viewer.setChecked( r, true );
+ viewer.setGrayed( r, false );
+ handleStateChanged(r, true, false, false);
+ }
+ else {
+ SigilCore.error( "Unknown path " + path );
+ }
+ }
+ }
+ }
+
+ protected void syncResourceModel(IResource element, boolean checked) {
+ try {
+ if ( dl == null ) {
+ dl = ModelElementFactory.getInstance().newModelElement(IDownloadJar.class);
+ getProjectModel().getBundle().setDownloadJar(dl);
+ }
+
+ if ( checked ) {
+ dl.addEntry( element.getProjectRelativePath() );
+ }
+ else {
+ dl.removeEntry( element.getProjectRelativePath() );
+ }
+
+ markDirty();
+ }
+ catch (ModelElementFactoryException e) {
+ SigilCore.error( "Failed to create model element", e );
+ }
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ExcludedResourcesFilter.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ExcludedResourcesFilter.java
new file mode 100644
index 0000000..4f91445
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ExcludedResourcesFilter.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.preferences.PrefsUtils;
+import org.cauldron.sigil.utils.GlobCompiler;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerFilter;
+
+public class ExcludedResourcesFilter extends ViewerFilter {
+
+ private final Set<Pattern> exclusionSet = new HashSet<Pattern>();
+
+ public ExcludedResourcesFilter() {
+ loadExclusions();
+ }
+
+ public final synchronized void loadExclusions() {
+ exclusionSet.clear();
+ IPreferenceStore store = SigilCore.getDefault().getPreferenceStore();
+ String[] exclusions = PrefsUtils.stringToArray(store.getString(SigilCore.DEFAULT_EXCLUDED_RESOURCES));
+ for (String exclusion : exclusions) {
+ exclusionSet.add(GlobCompiler.compile(exclusion));
+ }
+ }
+
+ @Override
+ public synchronized boolean select(Viewer viewer, Object parentElement, Object element) {
+ IResource file = (IResource) element;
+ String path = file.getName();
+ for ( Pattern p :exclusionSet ) {
+ if ( p.matcher(path).matches() ) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ExportPackagesSection.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ExportPackagesSection.java
new file mode 100644
index 0000000..406a5e5
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ExportPackagesSection.java
@@ -0,0 +1,176 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.ModelElementFactoryException;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.common.VersionRangeBoundingRule;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.form.SigilPage;
+import org.cauldron.sigil.ui.preferences.OptionalPrompt;
+import org.cauldron.sigil.ui.util.DefaultTableProvider;
+import org.cauldron.sigil.ui.util.ResourcesDialogHelper;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jdt.core.IPackageFragment;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.viewers.IContentProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.osgi.framework.Version;
+
+public class ExportPackagesSection extends BundleDependencySection {
+
+ public ExportPackagesSection(SigilPage page, Composite parent, ISigilProjectModel project) throws CoreException {
+ super( page, parent, project );
+ }
+
+ @Override
+ protected String getTitle() {
+ return "Export Packages";
+ }
+
+ @Override
+ protected Label createLabel(Composite parent, FormToolkit toolkit) {
+ return toolkit.createLabel( parent, "Specify which packages this bundle shares with other bundles." );
+ }
+
+ @Override
+ protected IContentProvider getContentProvider() {
+ return new DefaultTableProvider() {
+ public Object[] getElements(Object inputElement) {
+ return getBundle().getBundleInfo().getExports().toArray();
+ }
+ };
+ }
+
+ @Override
+ protected void handleAdd() {
+ NewPackageExportDialog dialog = ResourcesDialogHelper.createNewExportDialog(getSection().getShell(), "Add Exported Package", null, getProjectModel(), true);
+
+ if ( dialog.open() == Window.OK ) {
+ try {
+ // Add selected exports
+ boolean exportsAdded = false;
+
+ List<IPackageFragment> newPkgFragments = dialog.getSelectedElements();
+ for (IPackageFragment pkgFragment : newPkgFragments) {
+ IPackageExport pkgExport = ModelElementFactory.getInstance().newModelElement(IPackageExport.class);
+ pkgExport.setPackageName(pkgFragment.getElementName());
+ pkgExport.setVersion(dialog.getVersion());
+ getBundle().getBundleInfo().addExport(pkgExport);
+
+ exportsAdded = true;
+ }
+
+ // Add corresponding imports (maybe)
+ boolean importsAdded = false;
+
+ IPreferenceStore store = SigilCore.getDefault().getPreferenceStore();
+ boolean shouldAddImports = OptionalPrompt.optionallyPrompt(store, SigilCore.PREFERENCES_ADD_IMPORT_FOR_EXPORT, "Add Exports", "Should corresponding imports be added?", getSection().getShell());
+ if(shouldAddImports) {
+ for (IPackageFragment pkgFragment : newPkgFragments) {
+ IPackageImport pkgImport = ModelElementFactory.getInstance().newModelElement(IPackageImport.class);
+ pkgImport.setPackageName(pkgFragment.getElementName());
+ VersionRangeBoundingRule lowerBound = VersionRangeBoundingRule.valueOf(store.getString(SigilCore.DEFAULT_VERSION_LOWER_BOUND));
+ VersionRangeBoundingRule upperBound = VersionRangeBoundingRule.valueOf(store.getString(SigilCore.DEFAULT_VERSION_UPPER_BOUND));
+ Version version = dialog.getVersion();
+ if(version == null) {
+ version = getBundle().getVersion();
+ }
+ VersionRange versionRange = VersionRange.newInstance(version, lowerBound, upperBound);
+ pkgImport.setVersions(versionRange);
+
+ getBundle().getBundleInfo().addImport(pkgImport);
+
+ importsAdded = true;
+ }
+ }
+
+ if(importsAdded) {
+ ((SigilProjectEditorPart) getPage().getEditor()).refreshAllPages();
+ markDirty();
+ } else if(exportsAdded) {
+ refresh();
+ markDirty();
+ }
+ }
+ catch (ModelElementFactoryException e) {
+ SigilCore.error( "Failed to buiild model element for package export", e);
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected void handleEdit() {
+ IStructuredSelection selection = (IStructuredSelection) getSelection();
+
+ boolean changed = false;
+
+ if ( !selection.isEmpty() ) {
+ for ( Iterator<IPackageExport> i = selection.iterator(); i.hasNext(); ) {
+ IPackageExport packageExport = i.next();
+ NewPackageExportDialog dialog = ResourcesDialogHelper.createNewExportDialog(getSection().getShell(), "Edit Imported Package", packageExport, getProjectModel(), false);
+ if ( dialog.open() == Window.OK ) {
+ changed = true;
+ IPackageFragment pkgFragment = dialog.getSelectedElement();
+ packageExport.setPackageName(pkgFragment.getElementName());
+ packageExport.setVersion(dialog.getVersion());
+ }
+ }
+ }
+
+ if ( changed ) {
+ refresh();
+ markDirty();
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected void handleRemoved() {
+ IStructuredSelection selection = (IStructuredSelection) getSelection();
+
+ if ( !selection.isEmpty() ) {
+ for ( Iterator<IPackageExport> i = selection.iterator(); i.hasNext(); ) {
+ getBundle().getBundleInfo().removeExport( i.next() );
+ }
+
+ refresh();
+ markDirty();
+ }
+ }
+
+ private ISigilBundle getBundle() {
+ return getProjectModel().getBundle();
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ExportsForm.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ExportsForm.java
new file mode 100644
index 0000000..7a6fe3c
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ExportsForm.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.form.SigilPage;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.forms.IManagedForm;
+import org.eclipse.ui.forms.editor.FormEditor;
+import org.eclipse.ui.forms.widgets.ScrolledForm;
+import org.eclipse.ui.forms.widgets.TableWrapData;
+import org.eclipse.ui.forms.widgets.TableWrapLayout;
+
+public class ExportsForm extends SigilPage {
+
+ public static final String PAGE_ID = "exports";
+
+ private ISigilProjectModel project;
+
+ public ExportsForm(FormEditor editor, ISigilProjectModel project) {
+ super(editor, PAGE_ID, "Exports");
+ this.project = project;
+ }
+
+ @Override
+ protected void createFormContent(IManagedForm managedForm) {
+ ScrolledForm form = managedForm.getForm();
+ form.setText( "Exports" );
+
+ Composite body = form.getBody();
+ TableWrapLayout layout = new TableWrapLayout();
+ layout.bottomMargin = 10;
+ layout.topMargin = 5;
+ layout.leftMargin = 10;
+ layout.rightMargin = 10;
+ layout.numColumns = 1;
+ layout.horizontalSpacing = 10;
+ layout.verticalSpacing = 20;
+ body.setLayout(layout);
+ body.setLayoutData(new TableWrapData(TableWrapData.FILL));
+
+ try {
+ ExportPackagesSection exportPackages = new ExportPackagesSection( this, body, project );
+ managedForm.addPart( exportPackages );
+ }
+ catch (CoreException e) {
+ SigilCore.error( "Failed to create contents form", e);
+ }
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/GeneralInfoSection.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/GeneralInfoSection.java
new file mode 100644
index 0000000..4200712
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/GeneralInfoSection.java
@@ -0,0 +1,258 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.form.IFormValueConverter;
+import org.cauldron.sigil.ui.form.SigilFormEntry;
+import org.cauldron.sigil.ui.form.SigilFormEntryAdapter;
+import org.cauldron.sigil.ui.form.SigilPage;
+import org.cauldron.sigil.ui.form.SigilSection;
+import org.cauldron.sigil.ui.util.BackgroundLoadingSelectionDialog;
+import org.cauldron.sigil.ui.util.ResourcesDialogHelper;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.Section;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.Version;
+
+/**
+ * @author dave
+ *
+ */
+public class GeneralInfoSection extends SigilSection {
+
+ private String name;
+ private String symbolicName;
+ private Version version;
+ private String description;
+ private String provider;
+ private String activator;
+ private IRequiredBundle fragmentHost;
+
+ private SigilFormEntry nameEntry;
+ private SigilFormEntry symbolicNameEntry;
+ private SigilFormEntry versionEntry;
+ private SigilFormEntry descriptionEntry;
+ private SigilFormEntry providerEntry;
+ private SigilFormEntry activatorEntry;
+ private SigilFormEntry fragmentHostEntry;
+
+ /**
+ * @param parent
+ * @param toolkit
+ * @param style
+ * @throws CoreException
+ */
+ public GeneralInfoSection(SigilPage page, Composite parent, ISigilProjectModel project) throws CoreException {
+ super( page, parent, project );
+ }
+
+ protected void createSection(Section section,FormToolkit toolkit ) {
+ setTitle("General Information");
+
+ Composite body = createGridBody(3, false, toolkit);
+
+ Label label = toolkit.createLabel( body, "This section describes general information about this project." );
+ label.setLayoutData( new GridData(SWT.LEFT, SWT.CENTER, true, false, 3, 1) );
+
+ symbolicNameEntry = new SigilFormEntry(body, toolkit, "Symbolic Name");
+ symbolicNameEntry.setFormEntryListener( new SigilFormEntryAdapter() {
+ @Override
+ public void textValueChanged(SigilFormEntry form) {
+ symbolicName = nullIfEmpty((String) form.getValue());
+ checkDirty();
+ }
+ });
+
+ nameEntry = new SigilFormEntry(body, toolkit, "Name");
+ nameEntry.setFormEntryListener( new SigilFormEntryAdapter() {
+ @Override
+ public void textValueChanged(SigilFormEntry form) {
+ name = nullIfEmpty((String) form.getValue());
+ checkDirty();
+ }
+ });
+
+ descriptionEntry = new SigilFormEntry(body, toolkit, "Description");
+ descriptionEntry.setFormEntryListener( new SigilFormEntryAdapter() {
+ @Override
+ public void textValueChanged(SigilFormEntry form) {
+ description = nullIfEmpty((String) form.getValue());
+ checkDirty();
+ }
+ });
+
+ IFormValueConverter converter = new IFormValueConverter() {
+ public String getLabel(Object value) {
+ Version v = (Version) value;
+ return v.toString();
+ }
+
+ public Object getValue(String label) {
+ return Version.parseVersion(label);
+ }
+ };
+
+ versionEntry = new SigilFormEntry(body, toolkit, "Version", null, converter);
+ versionEntry.setFormEntryListener( new SigilFormEntryAdapter() {
+ @Override
+ public void textValueChanged(SigilFormEntry form) {
+ version = (Version) form.getValue();
+ checkDirty();
+ }
+ });
+
+ providerEntry = new SigilFormEntry(body, toolkit, "Provider");
+ providerEntry.setFormEntryListener( new SigilFormEntryAdapter() {
+ @Override
+ public void textValueChanged(SigilFormEntry form) {
+ provider = nullIfEmpty((String) form.getValue());
+ checkDirty();
+ }
+ });
+
+ activatorEntry = new SigilFormEntry(body, toolkit, "Bundle Activator", "Browse...", null);
+ activatorEntry.setFormEntryListener( new SigilFormEntryAdapter() {
+ @Override
+ public void textValueChanged(SigilFormEntry form) {
+ activator = (String) form.getValue();
+ checkDirty();
+ }
+
+ @Override
+ public void browseButtonSelected(SigilFormEntry form) {
+ BackgroundLoadingSelectionDialog<String> dialog = ResourcesDialogHelper.createClassSelectDialog(getShell(), "Add Bundle Activator", getProjectModel(), activator, BundleActivator.class.getName());
+
+ if (dialog.open() == Window.OK) {
+ form.setValue(dialog.getSelectedElement());
+ }
+ }
+ });
+
+ converter = new IFormValueConverter() {
+ public String getLabel(Object value) {
+ IRequiredBundle b = (IRequiredBundle) value;
+ return b == null ? null : b.getSymbolicName() + " " + b.getVersions();
+ }
+
+ public Object getValue(String label) {
+ return null;
+ }
+ };
+
+ fragmentHostEntry = new SigilFormEntry(body, toolkit, "Fragment Host", "Browse...", converter );
+ fragmentHostEntry.setFormEntryListener( new SigilFormEntryAdapter() {
+ @Override
+ public void textValueChanged(SigilFormEntry form) {
+ fragmentHost = (IRequiredBundle) form.getValue();
+ checkDirty();
+ }
+
+ @Override
+ public void browseButtonSelected(SigilFormEntry form) {
+ NewResourceSelectionDialog<IBundleModelElement> dialog = ResourcesDialogHelper.createRequiredBundleDialog( getSection().getShell(), "Add Required Bundle", getProjectModel(), null, getBundle().getBundleInfo().getRequiredBundles() );
+
+ if (dialog.open() == Window.OK) {
+ IRequiredBundle required = ModelElementFactory.getInstance().newModelElement( IRequiredBundle.class );
+ required.setSymbolicName(dialog.getSelectedName());
+ required.setVersions(dialog.getSelectedVersions());
+ form.setValue(required);
+ }
+ }
+ });
+ fragmentHostEntry.setFreeText(false);
+ }
+
+ private static String nullIfEmpty(String value) {
+ if ( value.trim().length() == 0 ) {
+ return null;
+ }
+ return value;
+ }
+
+ private Shell getShell() {
+ return getSection().getShell();
+ }
+
+ @Override
+ public void commit(boolean onSave) {
+ getBundle().getBundleInfo().setSymbolicName( symbolicName );
+ getBundle().getBundleInfo().setName( name );
+ getBundle().getBundleInfo().setVersion( version );
+ getBundle().getBundleInfo().setDescription( description );
+ getBundle().getBundleInfo().setVendor( provider );
+ getBundle().getBundleInfo().setFragmentHost(fragmentHost);
+ getBundle().getBundleInfo().setActivator(activator);
+
+ super.commit(onSave);
+ }
+
+ @Override
+ public void refresh() {
+ symbolicName = getProjectModel().getBundle().getBundleInfo().getSymbolicName();
+ name = getProjectModel().getBundle().getBundleInfo().getName();
+ description = getProjectModel().getBundle().getBundleInfo().getDescription();
+ version = getProjectModel().getBundle().getBundleInfo().getVersion();
+ provider = getProjectModel().getBundle().getBundleInfo().getVendor();
+ fragmentHost = getProjectModel().getBundle().getBundleInfo().getFragmentHost();
+ activator = getProjectModel().getBundle().getBundleInfo().getActivator();
+
+ nameEntry.setValue(name);
+ symbolicNameEntry.setValue(symbolicName);
+ versionEntry.setValue(version);
+ descriptionEntry.setValue(description);
+ providerEntry.setValue(provider);
+ fragmentHostEntry.setValue(fragmentHost);
+ activatorEntry.setValue(activator);
+
+ super.refresh();
+ }
+
+ private void checkDirty() {
+ boolean dirty = different(symbolicName, getProjectModel().getBundle().getBundleInfo().getSymbolicName() ) ||
+ different(name, getProjectModel().getBundle().getBundleInfo().getName() ) ||
+ different(version, getProjectModel().getBundle().getBundleInfo().getVersion() ) ||
+ different(description, getProjectModel().getBundle().getBundleInfo().getDescription()) ||
+ different(provider, getProjectModel().getBundle().getBundleInfo().getVendor()) ||
+ different(fragmentHost, getProjectModel().getBundle().getBundleInfo().getFragmentHost()) ||
+ different(activator, getProjectModel().getBundle().getBundleInfo().getActivator());
+
+ if ( dirty ) markDirty();
+ }
+
+ private boolean different(Object val1, Object val2) {
+ return val1 == null ? val2 != null : !val1.equals( val2 );
+ }
+
+ private ISigilBundle getBundle() {
+ return getProjectModel().getBundle();
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/IDependencyChecker.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/IDependencyChecker.java
new file mode 100644
index 0000000..556c5e9
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/IDependencyChecker.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+
+public interface IDependencyChecker {
+ boolean isSatisfied(IPackageImport packageImport);
+ boolean isSatisfied(IRequiredBundle requiredBundle);
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/IElementDescriptor.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/IElementDescriptor.java
new file mode 100644
index 0000000..4aa2953
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/IElementDescriptor.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+public interface IElementDescriptor<E> {
+ /**
+ * Return the short identifying name of the element.
+ */
+ String getName(E element);
+
+ /**
+ * Return a label for the element, including the name but possibly supplying
+ * additional information.
+ *
+ * @param element
+ * @return
+ */
+ String getLabel(E element);
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ImportPackagesSection.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ImportPackagesSection.java
new file mode 100644
index 0000000..9403075
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ImportPackagesSection.java
@@ -0,0 +1,153 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IPackageModelElement;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.form.SigilPage;
+import org.cauldron.sigil.ui.util.DefaultTableProvider;
+import org.cauldron.sigil.ui.util.ResourcesDialogHelper;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.viewers.IContentProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+
+public class ImportPackagesSection extends BundleDependencySection {
+
+ public ImportPackagesSection(SigilPage page, Composite parent, ISigilProjectModel project, Set<IModelElement> unresolvedPackages) throws CoreException {
+ super( page, parent, project, unresolvedPackages );
+ }
+
+ @Override
+ protected String getTitle() {
+ return "Import Packages";
+ }
+
+ @Override
+ protected Label createLabel(Composite parent, FormToolkit toolkit) {
+ return toolkit.createLabel( parent, "Specify which packages this bundle imports from other bundles." );
+ }
+
+
+ @Override
+ protected IContentProvider getContentProvider() {
+ return new DefaultTableProvider() {
+ public Object[] getElements(Object inputElement) {
+ ArrayList<IPackageImport> imports = new ArrayList<IPackageImport>(getBundle().getBundleInfo().getImports());
+ Collections.sort(imports, new Comparator<IPackageImport>() {
+ public int compare(IPackageImport o1, IPackageImport o2) {
+ return o1.getPackageName().compareTo( o2.getPackageName() );
+ }
+ });
+ return imports.toArray();
+ }
+ };
+ }
+
+ protected ISigilBundle getBundle() {
+ return getProjectModel().getBundle();
+ }
+
+ @Override
+ protected void handleAdd() {
+ NewResourceSelectionDialog<? extends IPackageModelElement> dialog =
+ ResourcesDialogHelper.createImportDialog(
+ getSection().getShell(),
+ "Add Imported Package",
+ getProjectModel(),
+ null,
+ getBundle().getBundleInfo().getImports() );
+
+ if ( dialog.open() == Window.OK ) {
+ IPackageImport pi = ModelElementFactory.getInstance().newModelElement(IPackageImport.class);
+ pi.setPackageName(dialog.getSelectedName());
+ pi.setVersions(dialog.getSelectedVersions());
+ pi.setOptional(dialog.isOptional());
+
+ getBundle().getBundleInfo().addImport(pi);
+ refresh();
+ markDirty();
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected void handleRemoved() {
+ IStructuredSelection selection = (IStructuredSelection) getSelection();
+
+ if ( !selection.isEmpty() ) {
+ for ( Iterator<IPackageImport> i = selection.iterator(); i.hasNext(); ) {
+ getBundle().getBundleInfo().removeImport( i.next() );
+ }
+
+ refresh();
+ markDirty();
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected void handleEdit() {
+ IStructuredSelection selection = (IStructuredSelection) getSelection();
+
+ boolean changed = false;
+
+ if ( !selection.isEmpty() ) {
+ for ( Iterator<IPackageImport> i = selection.iterator(); i.hasNext(); ) {
+ IPackageImport packageImport = i.next();
+ NewResourceSelectionDialog<? extends IPackageModelElement> dialog =
+ ResourcesDialogHelper.createImportDialog(
+ getSection().getShell(),
+ "Edit Imported Package",
+ getProjectModel(),
+ packageImport,
+ getBundle().getBundleInfo().getImports() );
+ if ( dialog.open() == Window.OK ) {
+ changed = true;
+ IPackageImport newImport = ModelElementFactory.getInstance().newModelElement(IPackageImport.class);
+ newImport.setPackageName(dialog.getSelectedName());
+ newImport.setVersions(dialog.getSelectedVersions());
+ newImport.setOptional(dialog.isOptional());
+
+ getBundle().getBundleInfo().removeImport( packageImport );
+ getBundle().getBundleInfo().addImport(newImport);
+ }
+ }
+ }
+
+ if ( changed ) {
+ refresh();
+ markDirty();
+ }
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/NewPackageExportDialog.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/NewPackageExportDialog.java
new file mode 100644
index 0000000..02289d6
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/NewPackageExportDialog.java
@@ -0,0 +1,150 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import java.util.Comparator;
+
+import org.cauldron.sigil.ui.util.BackgroundLoadingSelectionDialog;
+import org.eclipse.jdt.core.IPackageFragment;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+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.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+import org.osgi.framework.Version;
+
+public class NewPackageExportDialog extends BackgroundLoadingSelectionDialog<IPackageFragment> {
+
+ private static final IElementDescriptor<IPackageFragment> PKG_FRAGMENT_STRINGIFIER = new IElementDescriptor<IPackageFragment>() {
+ public String getLabel(IPackageFragment element) {
+ return getName(element);
+ }
+ public String getName(IPackageFragment element) {
+ return element.getElementName();
+ }
+ };
+
+ private static final Comparator<IPackageFragment> PKG_FRAGMENT_COMPARATOR = new Comparator<IPackageFragment>() {
+ public int compare(IPackageFragment o1, IPackageFragment o2) {
+ return o1.getElementName().compareTo(o2.getElementName());
+ }
+ };
+
+ private Version version = null;
+ private String error = null;
+ private Version projectVersion = new Version(0,0,0);
+
+ private Button btnInheritBundleVersion;
+ private Button btnExplicitVersion;
+ private Text txtVersion;
+
+
+ public NewPackageExportDialog(Shell parentShell, boolean multiSelect) {
+ super(parentShell, "Package:", multiSelect);
+ setDescriptor(PKG_FRAGMENT_STRINGIFIER);
+ setComparator(PKG_FRAGMENT_COMPARATOR);
+ }
+
+ @Override
+ protected Control createDialogArea(Composite parent) {
+ // Create controls
+ Composite container = (Composite) super.createDialogArea(parent);
+ Composite composite = new Composite(container, SWT.NONE);
+
+ Group grpVersion = new Group(composite, SWT.NONE);
+ grpVersion.setText("Version");
+
+ btnInheritBundleVersion = new Button(grpVersion, SWT.RADIO);
+ btnInheritBundleVersion.setText("Inherit bundle version");
+ new Label(grpVersion, SWT.NONE); // Spacer
+ btnExplicitVersion = new Button(grpVersion, SWT.RADIO);
+ btnExplicitVersion.setText("Fixed version:");
+ txtVersion = new Text(grpVersion, SWT.BORDER);
+
+ // Initialize
+ if(version == null) {
+ btnInheritBundleVersion.setSelection(true);
+ txtVersion.setEnabled(false);
+ txtVersion.setText(projectVersion.toString());
+ } else {
+ btnExplicitVersion.setSelection(true);
+ txtVersion.setEnabled(true);
+ txtVersion.setText(version.toString());
+ }
+ updateButtons();
+
+ // Listeners
+ Listener radioAndTextListener = new Listener() {
+ public void handleEvent(Event event) {
+ error = null;
+ if(btnInheritBundleVersion.getSelection()) {
+ version = null;
+ txtVersion.setEnabled(false);
+ } else {
+ txtVersion.setEnabled(true);
+ try {
+ version = new Version(txtVersion.getText());
+ } catch (IllegalArgumentException e) {
+ error = "Invalid version";
+ }
+ }
+ setErrorMessage(error);
+ updateButtons();
+ }
+ };
+ txtVersion.addListener(SWT.Modify, radioAndTextListener);
+ btnInheritBundleVersion.addListener(SWT.Selection, radioAndTextListener);
+ btnExplicitVersion.addListener(SWT.Selection, radioAndTextListener);
+
+
+ // Layout
+ composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ composite.setLayout(new GridLayout(1, false));
+ grpVersion.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ grpVersion.setLayout(new GridLayout(2, false));
+ txtVersion.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+
+ return container;
+ }
+
+ @Override
+ protected boolean canComplete() {
+ return super.canComplete() && error == null;
+ }
+
+ public void setProjectVersion(Version projectVersion) {
+ this.projectVersion = projectVersion;
+ }
+
+ public void setVersion(Version version) {
+ this.version = version;
+ }
+
+ public Version getVersion() {
+ return version;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/NewResourceSelectionDialog.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/NewResourceSelectionDialog.java
new file mode 100644
index 0000000..59812cc
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/NewResourceSelectionDialog.java
@@ -0,0 +1,161 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.common.VersionRangeBoundingRule;
+import org.cauldron.sigil.model.osgi.IVersionedModelElement;
+import org.cauldron.sigil.ui.util.BackgroundLoadingSelectionDialog;
+import org.cauldron.sigil.ui.util.IValidationListener;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.osgi.framework.Version;
+
+public class NewResourceSelectionDialog<E extends IVersionedModelElement> extends BackgroundLoadingSelectionDialog<E> {
+
+ private VersionRangeComponent pnlVersionRange;
+ private boolean optionalEnabled = true;
+ private Button btnOptional;
+
+ private VersionRange selectedVersions = null;
+ private boolean optional = false;
+
+ public NewResourceSelectionDialog(Shell parentShell, String selectionLabel, boolean multi) {
+ super(parentShell, selectionLabel, multi);
+ }
+
+ public void setOptionalEnabled(boolean enabled) {
+ optionalEnabled = enabled;
+ }
+
+ @Override
+ protected Control createDialogArea(Composite parent) {
+ // Create controls
+ Composite container = (Composite) super.createDialogArea(parent);
+ Composite composite = new Composite(container, SWT.NONE);
+
+ if ( optionalEnabled ) {
+ new Label(composite, SWT.NONE); //Spacer
+ btnOptional = new Button(composite, SWT.CHECK);
+ btnOptional.setText("Optional");
+ }
+
+ Label lblVersionRange = new Label(composite, SWT.NONE);
+ lblVersionRange.setText("Version Range:");
+ Group group = new Group(composite, SWT.BORDER);
+
+ pnlVersionRange = new VersionRangeComponent(group, SWT.NONE);
+
+ // Initialize
+ if (selectedVersions != null) {
+ pnlVersionRange.setVersions(selectedVersions);
+ }
+
+ if ( optionalEnabled ) {
+ btnOptional.setSelection(optional);
+ updateButtons();
+ }
+
+ // Hookup Listeners
+ pnlVersionRange.addVersionChangeListener(new VersionsChangeListener() {
+ public void versionsChanged(VersionRange range) {
+ selectedVersions = range;
+ updateButtons();
+ }
+ });
+ pnlVersionRange.addValidationListener(new IValidationListener() {
+ public void validationMessage(String message, int level) {
+ setMessage(message, level);
+ updateButtons();
+ }
+ });
+
+ if ( optionalEnabled ) {
+ btnOptional.addSelectionListener(new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ optional = btnOptional.getSelection();
+ }
+ });
+ }
+
+ // Layout
+ composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ GridLayout layout = new GridLayout(2, false);
+ layout.verticalSpacing = 10;
+ layout.horizontalSpacing = 10;
+ composite.setLayout(layout);
+
+ lblVersionRange.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false));
+ group.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ group.setLayout(new FillLayout());
+
+ return container;
+ }
+
+ @Override
+ protected void elementSelected(E selection) {
+ if(selection != null) {
+ IPreferenceStore store = SigilCore.getDefault().getPreferenceStore();
+ VersionRangeBoundingRule lowerBoundRule = VersionRangeBoundingRule.valueOf(store.getString(SigilCore.DEFAULT_VERSION_LOWER_BOUND));
+ VersionRangeBoundingRule upperBoundRule = VersionRangeBoundingRule.valueOf(store.getString(SigilCore.DEFAULT_VERSION_UPPER_BOUND));
+
+ Version version = selection.getVersion();
+ selectedVersions = VersionRange.newInstance(version, lowerBoundRule, upperBoundRule);
+ pnlVersionRange.setVersions(selectedVersions);
+ }
+ }
+
+ @Override
+ protected synchronized boolean canComplete() {
+ return super.canComplete() && selectedVersions != null;
+ }
+
+ public VersionRange getSelectedVersions() {
+ return selectedVersions;
+ }
+
+ public void setVersions(VersionRange versions) {
+ selectedVersions = versions;
+ if(pnlVersionRange != null && !pnlVersionRange.isDisposed()) {
+ pnlVersionRange.setVersions(versions);
+ updateButtons();
+ }
+ }
+
+ public boolean isOptional() {
+ return optional;
+ }
+
+ public void setOptional(boolean optional) {
+ this.optional = optional;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/OverviewForm.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/OverviewForm.java
new file mode 100644
index 0000000..a948427
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/OverviewForm.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.form.SigilPage;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.forms.IManagedForm;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.ScrolledForm;
+import org.eclipse.ui.forms.widgets.TableWrapData;
+import org.eclipse.ui.forms.widgets.TableWrapLayout;
+
+/**
+ * @author dave
+ *
+ */
+public class OverviewForm extends SigilPage {
+ public static final String PAGE_ID = "overview";
+ private ISigilProjectModel sigil;
+
+ public OverviewForm(SigilProjectEditorPart editor, ISigilProjectModel sigil) {
+ super(editor, PAGE_ID, "Overview");
+ this.sigil = sigil;
+ }
+
+ @Override
+ protected void createFormContent(IManagedForm managedForm) {
+ FormToolkit toolkit = managedForm.getToolkit();
+
+ ScrolledForm form = managedForm.getForm();
+ form.setText( "Overview" );
+
+ Composite body = form.getBody();
+ TableWrapLayout layout = new TableWrapLayout();
+ layout.bottomMargin = 10;
+ layout.topMargin = 5;
+ layout.leftMargin = 10;
+ layout.rightMargin = 10;
+ layout.numColumns = 2;
+ layout.horizontalSpacing = 10;
+ body.setLayout(layout);
+ body.setLayoutData(new TableWrapData(TableWrapData.FILL));
+
+ Composite left = toolkit.createComposite(body);
+ layout = new TableWrapLayout();
+ layout.verticalSpacing = 20;
+ left.setLayout(layout);
+ left.setLayoutData(new TableWrapData(TableWrapData.FILL_GRAB));
+
+ Composite right = toolkit.createComposite(body);
+ layout = new TableWrapLayout();
+ layout.verticalSpacing = 20;
+ right.setLayout(layout);
+ right.setLayoutData( new TableWrapData( TableWrapData.FILL_GRAB) );
+
+ try {
+ GeneralInfoSection general = new GeneralInfoSection(this, left, sigil);
+ managedForm.addPart( general );
+
+ ContentSummarySection content = new ContentSummarySection( this, right, sigil);
+ managedForm.addPart( content );
+
+ // XXX-FELIX
+ // commented out due to removal of runtime newton integration
+ // potential to bring back in medium term...
+ //TestingSection testing = new TestingSection(this, right, newton);
+ //managedForm.addPart(testing);
+
+ ToolsSection tools = new ToolsSection(this, right, sigil);
+ managedForm.addPart(tools);
+ }
+ catch (CoreException e) {
+ SigilCore.error( "Failed to create overview form", e );
+ }
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/PackageExportDialog.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/PackageExportDialog.java
new file mode 100644
index 0000000..80c2328
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/PackageExportDialog.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import org.eclipse.jface.viewers.IContentProvider;
+import org.eclipse.jface.viewers.ViewerFilter;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.KeyAdapter;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+import org.osgi.framework.Version;
+
+public class PackageExportDialog extends ResourceSelectDialog {
+
+ private Text versionText;
+ private Version version;
+
+ public PackageExportDialog(Shell parentShell, String title, IContentProvider content, ViewerFilter filter, Object scope ) {
+ super(parentShell, content, filter, scope, title, "Package Name:", true);
+ }
+
+ @Override
+ protected void createCustom(Composite body) {
+ Label l = new Label( body, SWT.NONE );
+ l.setText( "Version:" );
+ versionText = new Text(body, SWT.BORDER);
+ versionText.addKeyListener(new KeyAdapter() {
+ @Override
+ public void keyReleased(KeyEvent e) {
+ try {
+ version = Version.parseVersion(versionText.getText());
+ setErrorMessage(null);
+ }
+ catch (IllegalArgumentException ex) {
+ setErrorMessage("Invalid version");
+ }
+ }
+ });
+ if ( version != null ) {
+ versionText.setText( version.toString() );
+ }
+ }
+
+ public Version getVersion() {
+ return version;
+ }
+
+ public void setVersion(Version version) {
+ this.version = version;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ProjectLabelProvider.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ProjectLabelProvider.java
new file mode 100644
index 0000000..ca745f5
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ProjectLabelProvider.java
@@ -0,0 +1,178 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import java.io.InputStream;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.cauldron.sigil.ui.util.DefaultLabelProvider;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.IPackageFragment;
+import org.eclipse.jdt.core.IPackageFragmentRoot;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jface.resource.ImageRegistry;
+import org.eclipse.jface.viewers.IBaseLabelProvider;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.widgets.Widget;
+
+public class ProjectLabelProvider extends DefaultLabelProvider implements IBaseLabelProvider {
+
+ private final Widget parent;
+ private final ImageRegistry registry;
+ private final IDependencyChecker checker;
+
+ public ProjectLabelProvider(Widget parent, ImageRegistry registry ) {
+ this(parent, registry, null);
+ }
+
+ public ProjectLabelProvider(Widget parent, ImageRegistry registry, IDependencyChecker checker) {
+ this.parent = parent;
+ this.registry = registry;
+ this.checker = checker;
+ }
+
+ public Image getImage(Object element) {
+ Image result = null;
+ if ( element instanceof ISigilBundle || element instanceof IRequiredBundle || element instanceof IBundleModelElement) {
+ result = findBundle();
+ }
+ else if (element instanceof IPackageImport) {
+ if(checker != null) {
+ result = checker.isSatisfied((IPackageImport) element) ? findPackage() : findPackageError();
+ } else {
+ result = findPackage();
+ }
+ }
+ else if (element instanceof IPackageExport) {
+ result = findPackage();
+ }
+ else if ( element instanceof IPackageFragmentRoot ) {
+ IPackageFragmentRoot root = (IPackageFragmentRoot) element;
+ try {
+ if ( root.getKind() == IPackageFragmentRoot.K_SOURCE ) {
+ result = findPackage();
+ }
+ else {
+ result = findBundle();
+ }
+ } catch (JavaModelException e) {
+ SigilCore.error( "Failed to inspect package fragment root", e );
+ }
+ }
+ else if ( element instanceof IClasspathEntry ) {
+ result = findPackage();
+ }
+
+ return result;
+ }
+
+ public String getText(Object element) {
+ if ( element instanceof ISigilBundle ) {
+ ISigilBundle bundle = (ISigilBundle) element;
+ return bundle.getBundleInfo().getSymbolicName();
+ }
+
+ if ( element instanceof IRequiredBundle ) {
+ IRequiredBundle req = (IRequiredBundle) element;
+ return req.getSymbolicName() + " " + req.getVersions();
+ }
+
+ if ( element instanceof IPackageImport ) {
+ IPackageImport req = (IPackageImport) element;
+ return req.getPackageName() + " " + req.getVersions();
+ }
+
+ if ( element instanceof IPackageExport ) {
+ IPackageExport pe = (IPackageExport) element;
+ return pe.getPackageName() + " " + pe.getVersion();
+ }
+
+ if ( element instanceof IResource ) {
+ IResource resource = (IResource) element;
+ return resource.getName();
+ }
+
+ if ( element instanceof IPackageFragment ) {
+ IPackageFragment f = (IPackageFragment) element;
+ return f.getElementName();
+ }
+
+ if ( element instanceof IPackageFragmentRoot ) {
+ IPackageFragmentRoot f = (IPackageFragmentRoot) element;
+ try {
+ return f.getUnderlyingResource().getName();
+ } catch (JavaModelException e) {
+ return "unknown";
+ }
+ }
+
+ if ( element instanceof IClasspathEntry ) {
+ IClasspathEntry cp = (IClasspathEntry) element;
+ return cp.getPath().toString();
+ }
+
+ return element.toString();
+ }
+
+ private Image findPackage() {
+ Image image = registry.get( "package" );
+
+ if ( image == null ) {
+ image = loadImage( "icons/package_obj.png" );
+ registry.put( "package", image);
+ }
+
+ return image;
+ }
+
+ private Image findPackageError() {
+ Image image = registry.get("package_error");
+ if(image == null) {
+ image = loadImage("icons/package_obj_error.gif");
+ registry.put("package_error", image);
+ }
+ return image;
+ }
+
+ private Image findBundle() {
+ Image image = registry.get( "bundle" );
+
+ if ( image == null ) {
+ image = loadImage( "icons/jar_obj.png" );
+ registry.put( "bundle", image);
+ }
+
+ return image;
+ }
+
+ private Image loadImage(String resource) {
+ InputStream in = ProjectLabelProvider.class.getClassLoader().getResourceAsStream( resource );
+ ImageData data = new ImageData( in );
+ return new Image( parent.getDisplay(), data );
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ProjectTableViewer.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ProjectTableViewer.java
new file mode 100644
index 0000000..362de5b
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ProjectTableViewer.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import java.util.Set;
+
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.ui.util.ModelLabelProvider;
+import org.eclipse.jface.viewers.IContentProvider;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.swt.widgets.Table;
+
+public class ProjectTableViewer extends TableViewer {
+
+ private ModelLabelProvider labelProvider;
+
+ public ProjectTableViewer(Table table) {
+ super(table);
+ labelProvider = new ModelLabelProvider();
+ setLabelProvider(labelProvider);
+ }
+
+ @Override
+ public void setContentProvider(IContentProvider provider) {
+ super.setContentProvider(provider);
+ setInput(getTable());
+ }
+
+ public void setUnresolvedElements(Set<? extends IModelElement> elements) {
+ labelProvider.setUnresolvedElements(elements);
+ }
+
+ @Override
+ public ModelLabelProvider getLabelProvider() {
+ return labelProvider;
+ }
+
+
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/PropertiesForm.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/PropertiesForm.java
new file mode 100644
index 0000000..1e069fd
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/PropertiesForm.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.eclipse.jdt.internal.ui.JavaPlugin;
+import org.eclipse.jdt.internal.ui.propertiesfileeditor.IPropertiesFilePartitions;
+import org.eclipse.jdt.internal.ui.propertiesfileeditor.PropertiesFileSourceViewerConfiguration;
+import org.eclipse.jdt.ui.text.JavaTextTools;
+import org.eclipse.jface.preference.IPreferenceStore;
+
+@SuppressWarnings("restriction")
+public class PropertiesForm extends SigilSourcePage {
+
+ public static final String PAGE_ID = "properties";
+
+ public PropertiesForm(SigilProjectEditorPart editor, ISigilProjectModel project) {
+ super(PAGE_ID);
+ JavaTextTools textTools= JavaPlugin.getDefault().getJavaTextTools();
+ IPreferenceStore store= JavaPlugin.getDefault().getCombinedPreferenceStore();
+ setPreferenceStore(store);
+ setSourceViewerConfiguration(new PropertiesFileSourceViewerConfiguration(textTools.getColorManager(), store, this, IPropertiesFilePartitions.PROPERTIES_FILE_PARTITIONING));
+ /*IFileEditorInput fileInput = (IFileEditorInput) editor.getEditorInput();
+ this.setDocumentProvider(fileInput);*/
+ }
+
+ @Override
+ public boolean isSaveAsAllowed() {
+ return false;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/RequiresBundleSection.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/RequiresBundleSection.java
new file mode 100644
index 0000000..5adf84d
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/RequiresBundleSection.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+
+import java.util.Iterator;
+import java.util.Set;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.ModelElementFactoryException;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.form.SigilPage;
+import org.cauldron.sigil.ui.util.DefaultTableProvider;
+import org.cauldron.sigil.ui.util.ResourcesDialogHelper;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.viewers.IContentProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+
+public class RequiresBundleSection extends BundleDependencySection {
+
+ public RequiresBundleSection(SigilPage page, Composite parent, ISigilProjectModel project, Set<IModelElement> unresolvedElements) throws CoreException {
+ super( page, parent, project, unresolvedElements );
+ }
+
+ @Override
+ protected String getTitle() {
+ return "Requires Bundles";
+ }
+
+ @Override
+ protected Label createLabel(Composite parent, FormToolkit toolkit) {
+ return toolkit.createLabel( parent, "Specify which bundles this bundle depends on." );
+ }
+
+ @Override
+ protected IContentProvider getContentProvider() {
+ return new DefaultTableProvider() {
+ public Object[] getElements(Object inputElement) {
+ return getBundle().getBundleInfo().getRequiredBundles().toArray();
+ }
+ };
+ }
+
+ protected ISigilBundle getBundle() {
+ return getProjectModel().getBundle();
+ }
+
+ @Override
+ protected void handleAdd() {
+ try {
+ NewResourceSelectionDialog<IBundleModelElement> dialog = ResourcesDialogHelper.createRequiredBundleDialog( getSection().getShell(), "Add Required Bundle", getProjectModel(), null, getBundle().getBundleInfo().getRequiredBundles() );
+
+ if (dialog.open() == Window.OK) {
+ IRequiredBundle required = ModelElementFactory.getInstance().newModelElement( IRequiredBundle.class );
+ required.setSymbolicName(dialog.getSelectedName());
+ required.setVersions(dialog.getSelectedVersions());
+ required.setOptional(dialog.isOptional());
+
+ getBundle().getBundleInfo().addRequiredBundle(required);
+ refresh();
+ markDirty();
+ }
+ }
+ catch (ModelElementFactoryException e) {
+ SigilCore.error( "Failed to build required bundle", e );
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected void handleEdit() {
+ IStructuredSelection selection = (IStructuredSelection) getSelection();
+
+ boolean changed = false;
+
+ if ( !selection.isEmpty() ) {
+ for ( Iterator<IRequiredBundle> i = selection.iterator(); i.hasNext(); ) {
+ IRequiredBundle requiredBundle = i.next();
+ NewResourceSelectionDialog<IBundleModelElement> dialog = ResourcesDialogHelper.createRequiredBundleDialog(getSection().getShell(), "Edit Imported Package", getProjectModel(), requiredBundle, getBundle().getBundleInfo().getRequiredBundles() );
+ if ( dialog.open() == Window.OK ) {
+ changed = true;
+ requiredBundle.setSymbolicName(dialog.getSelectedName());
+ requiredBundle.setVersions(dialog.getSelectedVersions());
+ requiredBundle.setOptional(dialog.isOptional());
+ }
+ }
+ }
+
+ if ( changed ) {
+ refresh();
+ markDirty();
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected void handleRemoved() {
+ IStructuredSelection selection = (IStructuredSelection) getSelection();
+
+ if ( !selection.isEmpty() ) {
+ for ( Iterator<IRequiredBundle> i = selection.iterator(); i.hasNext(); ) {
+ getBundle().getBundleInfo().removeRequiredBundle( i.next() );
+ }
+
+ refresh();
+ markDirty();
+ }
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ResourceBuildSection.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ResourceBuildSection.java
new file mode 100644
index 0000000..efd61d6
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ResourceBuildSection.java
@@ -0,0 +1,169 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.SigilUI;
+import org.cauldron.sigil.ui.form.SigilPage;
+import org.cauldron.sigil.ui.util.ModelLabelProvider;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceChangeListener;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.jface.preference.PreferenceDialog;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.jface.viewers.CheckboxTreeViewer;
+import org.eclipse.jface.viewers.ICheckStateListener;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Link;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.ui.dialogs.PreferencesUtil;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.Section;
+import org.eclipse.ui.forms.widgets.TableWrapData;
+
+/**
+ * @author dave
+ *
+ */
+public class ResourceBuildSection extends AbstractResourceSection implements ICheckStateListener, IResourceChangeListener, IPropertyChangeListener {
+
+ private ExcludedResourcesFilter resourcesFilter;
+
+ /**
+ * @param page
+ * @param parent
+ * @param project
+ * @throws CoreException
+ */
+ public ResourceBuildSection(SigilPage page, Composite parent, ISigilProjectModel project) throws CoreException {
+ super( page, parent, project );
+ }
+
+ /* (non-Javadoc)
+ * @see org.cauldron.sigil.ui.editors.project.SigilSection#createSection(org.eclipse.ui.forms.widgets.Section, org.eclipse.ui.forms.widgets.FormToolkit)
+ */
+ @Override
+ protected void createSection(Section section, FormToolkit toolkit) {
+ setTitle( "Resources" );
+
+ Composite body = createTableWrapBody(1, toolkit);
+
+ toolkit.createLabel( body, "Specify which resources are included in the bundle." );
+
+ tree = toolkit.createTree( body, SWT.CHECK | SWT.BORDER );
+ Link link = new Link(body, SWT.WRAP);
+ link.setText("Some resources may be filtered according to preferences. <a href=\"excludedResourcePrefs\">Click here</a> to edit the list of exclusions.");
+
+ TableWrapData data = new TableWrapData( TableWrapData.FILL_GRAB);
+ data.heightHint = 200;
+ tree.setLayoutData( data );
+
+ viewer = new CheckboxTreeViewer( tree );
+ IProject base = getProjectModel().getProject();
+ viewer.setContentProvider( new ContainerTreeProvider() );
+ viewer.setLabelProvider( new ModelLabelProvider() );
+ viewer.addCheckStateListener( this );
+ resourcesFilter = new ExcludedResourcesFilter();
+ viewer.addFilter(resourcesFilter);
+ viewer.setInput(base);
+
+ link.addListener(SWT.Selection, new Listener() {
+ public void handleEvent(Event event) {
+ if("excludedResourcePrefs".equals(event.text)) {
+ PreferenceDialog dialog = PreferencesUtil.createPreferenceDialogOn(getPage().getEditorSite().getShell(), SigilCore.EXCLUDED_RESOURCES_PREFERENCES_ID, null, null);
+ dialog.open();
+ }
+ }
+ });
+
+ SigilCore.getDefault().getPreferenceStore().addPropertyChangeListener(this);
+
+ startWorkspaceListener(base.getWorkspace());
+ }
+
+ @Override
+ public void commit(boolean onSave) {
+ ISigilBundle bundle = getProjectModel().getBundle();
+
+ bundle.clearSourcePaths();
+
+ SigilUI.runInUISync( new Runnable() {
+ public void run() {
+ for ( Object o : viewer.getCheckedElements() ) {
+ if ( !viewer.getGrayed(o) ) {
+ IResource r = (IResource) o;
+ getProjectModel().getBundle().addSourcePath( r.getProjectRelativePath() );
+ }
+ }
+ }
+ });
+
+ super.commit(onSave);
+ }
+
+ @Override
+ protected void refreshSelections() {
+ // zero the state
+ for ( IPath path : getProjectModel().getBundle().getSourcePaths() ) {
+ IResource r = findResource( path );
+ if ( r != null ) {
+ viewer.expandToLevel(r, 0);
+ viewer.setChecked( r, true );
+ viewer.setGrayed( r, false );
+ handleStateChanged(r, true, false, false);
+ }
+ else {
+ SigilCore.error( "Unknown path " + path );
+ }
+ }
+ }
+
+ @Override
+ protected void syncResourceModel(IResource element, boolean checked) {
+ if ( checked ) {
+ getProjectModel().getBundle().addSourcePath( element.getProjectRelativePath() );
+ }
+ else {
+ getProjectModel().getBundle().removeSourcePath( element.getProjectRelativePath() );
+ }
+
+ markDirty();
+ }
+
+ @Override
+ public void dispose() {
+ super.dispose();
+ SigilCore.getDefault().getPreferenceStore().removePropertyChangeListener(this);
+ }
+
+ public void propertyChange(PropertyChangeEvent event) {
+ resourcesFilter.loadExclusions();
+ viewer.refresh();
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ResourceImportDialog.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ResourceImportDialog.java
new file mode 100644
index 0000000..e50fb21
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ResourceImportDialog.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.eclipse.jface.viewers.IContentProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.ViewerFilter;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Shell;
+
+public class ResourceImportDialog extends ResourceSelectDialog implements VersionsChangeListener {
+
+ private VersionRangeComponent versions;
+ private VersionRange range;
+
+ public ResourceImportDialog(Shell parentShell, String title, String label, IContentProvider content, ViewerFilter filter, Object scope ) {
+ super(parentShell, content, filter, scope, title, label, true);
+ }
+
+ public VersionRange getVersions() {
+ return range;
+ }
+
+ @Override
+ protected void createCustom(Composite body) {
+ versions = new VersionRangeComponent(body, SWT.BORDER );
+ versions.addVersionChangeListener(this);
+ versions.setVersions(range);
+
+ GridData data = new GridData( SWT.LEFT, SWT.TOP, true, true );
+ data.horizontalSpan = 2;
+ data.widthHint = 300;
+ data.heightHint = 200;
+ versions.setLayoutData(data);
+ }
+
+ @Override
+ protected void selectionChanged(SelectionChangedEvent event) {
+ if ( event.getSelection().isEmpty() ) {
+ versions.setEnabled(false);
+ }
+ else {
+ versions.setEnabled(true);
+ }
+ }
+
+ public void versionsChanged(VersionRange range) {
+ this.range = range;
+ if ( range == null ) {
+ setErrorMessage( "Invalid version" );
+ }
+ else {
+ setErrorMessage( null );
+ }
+ }
+
+ public void setVersions(VersionRange range) {
+ this.range = range;
+ }
+
+ public IPackageImport getImport() {
+ IPackageImport packageImport = ModelElementFactory.getInstance().newModelElement( IPackageImport.class );
+ packageImport.setPackageName( (String) getSelected()[0] );
+ packageImport.setVersions( getVersions() );
+ return packageImport;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ResourceSelectDialog.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ResourceSelectDialog.java
new file mode 100644
index 0000000..e967548
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ResourceSelectDialog.java
@@ -0,0 +1,372 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.cauldron.sigil.ui.util.ModelLabelProvider;
+import org.cauldron.sigil.ui.util.SingletonSelection;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.resource.StringConverter;
+import org.eclipse.jface.viewers.ComboViewer;
+import org.eclipse.jface.viewers.IContentProvider;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.StructuredViewer;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerComparator;
+import org.eclipse.jface.viewers.ViewerFilter;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTException;
+import org.eclipse.swt.events.KeyAdapter;
+import org.eclipse.swt.events.KeyEvent;
+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.Item;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.Text;
+
+public class ResourceSelectDialog extends Dialog {
+
+ private AtomicInteger keyCheck = new AtomicInteger();
+ private ScheduledExecutorService background = Executors.newSingleThreadScheduledExecutor();
+
+ private class UpdateViewerRunnable implements Runnable {
+ private int check;
+ public UpdateViewerRunnable(int check) {
+ this.check = check;
+ }
+
+ public void run() {
+ if ( check == keyCheck.get() ) {
+ try {
+ viewer.refresh();
+ }
+ catch (SWTException e) {
+ // discard
+ }
+ }
+ }
+ }
+
+ private static final ISelection EMPTY_SELECTION = new ISelection() {
+ public boolean isEmpty() {
+ return true;
+ }
+ };
+
+ private Job job;
+
+ private boolean isCombo;
+ private String title;
+ private String selectionText;
+
+ private StructuredViewer viewer;
+ private Combo resourceNameCombo;
+ private Table resourceNameTable;
+ private Text errorMessageText;
+ private String errorMessage;
+
+ private ViewerFilter filter;
+ private Object[] selected;
+
+ private Object scope;
+ private IContentProvider content;
+ private ILabelProvider labelProvider;
+
+ public ResourceSelectDialog(Shell parentShell, IContentProvider content, ViewerFilter filter, Object scope, String title, String selectionText, boolean isCombo) {
+ super(parentShell);
+ this.title = title;
+ this.selectionText = selectionText;
+ this.content = content;
+ this.filter = filter;
+ this.scope = scope;
+ this.isCombo = isCombo;
+ }
+
+ public void setJob(Job job) {
+ this.job = job;
+ }
+
+ public void refreshResources() {
+ try {
+ getShell().getDisplay().asyncExec( new Runnable() {
+ public void run() {
+ try {
+ viewer.refresh();
+ }
+ catch (SWTException e) {
+ // attempt to exec after dialog closed - discard
+ }
+ }
+ });
+ }
+ catch (NullPointerException e) {
+ // attempt to exec after dialog closed - discard
+ }
+ catch (SWTException e) {
+ // attempt to exec after dialog closed - discard
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.jface.window.Window#configureShell(org.eclipse.swt.widgets.Shell)
+ */
+ protected void configureShell(Shell shell) {
+ super.configureShell(shell);
+ if (title != null) {
+ shell.setText(title);
+ }
+ }
+
+ @Override
+ public void create() {
+ super.create();
+ if ( getItemCount() == 0 ) {
+ setErrorMessage( "No resources available" );
+ getButton(IDialogConstants.OK_ID).setEnabled(false);
+ }
+ else {
+ ISelection selection = selected == null ? EMPTY_SELECTION : new SingletonSelection( selected );
+ setSelected( new SelectionChangedEvent( viewer, selection ), true );
+ }
+
+ if ( job != null ) {
+ job.schedule();
+ }
+ }
+
+ @Override
+ public boolean close() {
+ if ( job != null ) {
+ job.cancel();
+ }
+ background.shutdownNow();
+ return super.close();
+ }
+
+ private int getItemCount() {
+ if ( isCombo ) {
+ return resourceNameCombo.getItemCount();
+ }
+ else {
+ return resourceNameTable.getItemCount();
+ }
+ }
+
+ @Override
+ protected Control createDialogArea(Composite parent) {
+ Composite body = (Composite) super.createDialogArea(parent);
+
+ GridLayout layout = (GridLayout) body.getLayout();
+ layout.numColumns = 2;
+ GridData data;
+
+ labelProvider = new ModelLabelProvider();
+
+ Label l = new Label( body, SWT.LEFT );
+ l.setText( selectionText );
+ data = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING);
+ if ( !isCombo ) {
+ data.horizontalSpan = 2;
+ }
+ l.setLayoutData( data );
+
+ if ( isCombo ) {
+ createCombo( body );
+ }
+ else {
+ createTable( body );
+ }
+
+ viewer.addFilter( filter );
+ viewer.setContentProvider(content);
+ viewer.setLabelProvider( getLabelProvider() );
+ viewer.setComparator( new ViewerComparator() );
+ viewer.setInput( scope );
+
+ viewer.addSelectionChangedListener( new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent event) {
+ setSelected(event, false);
+ }
+ });
+ createCustom( body );
+
+ errorMessageText = new Text(body, SWT.READ_ONLY | SWT.WRAP);
+ data = new GridData(GridData.GRAB_HORIZONTAL | GridData.HORIZONTAL_ALIGN_FILL);
+ data.horizontalSpan = 2;
+ errorMessageText.setLayoutData(data);
+ errorMessageText.setBackground(errorMessageText.getDisplay().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND));
+ setErrorMessage(errorMessage);
+
+ return body;
+ }
+
+ protected void createCustom(Composite body) {
+ }
+
+ private void createCombo(Composite body) {
+ resourceNameCombo = new Combo( body, SWT.SINGLE | SWT.BORDER );
+ GridData data = new GridData( GridData.HORIZONTAL_ALIGN_END);
+ data.widthHint = 200;
+ resourceNameCombo.setLayoutData( data );
+
+ viewer = new ComboViewer(resourceNameCombo);
+ }
+
+ private void createTable(Composite body) {
+ final Text txtFilter = new Text(body, SWT.BORDER);
+ GridData data = new GridData( GridData.HORIZONTAL_ALIGN_END);
+ data.horizontalSpan = 2;
+ data.widthHint = 400;
+ txtFilter.setLayoutData(data);
+
+ resourceNameTable = new Table( body, SWT.MULTI | SWT.BORDER );
+ data = new GridData( GridData.HORIZONTAL_ALIGN_END);
+ data.widthHint = 400;
+ data.heightHint = 400;
+ resourceNameTable.setLayoutData( data );
+
+ viewer = new TableViewer(resourceNameTable);
+
+ txtFilter.addKeyListener( new KeyAdapter() {
+ @Override
+ public void keyReleased(KeyEvent e) {
+ switch ( e.keyCode ) {
+ case SWT.ARROW_UP:
+ scrollTable(-1);
+ break;
+ case SWT.ARROW_DOWN:
+ scrollTable(+1);
+ break;
+ default:
+ Runnable r = new UpdateViewerRunnable(keyCheck.incrementAndGet());
+ background.schedule(r, 100, TimeUnit.MILLISECONDS);
+ break;
+ }
+ }
+ });
+
+ ViewerFilter filter = new ViewerFilter() {
+ @Override
+ public boolean select(Viewer viewer, Object parentElement,
+ Object element) {
+ return getLabelProvider().getText(element).startsWith( txtFilter.getText() );
+ }
+ };
+
+ viewer.addFilter(filter);
+ }
+
+ private void scrollTable(int delta) {
+ int i = resourceNameTable.getSelectionIndex();
+
+ if ( i == -1 ) {
+ if ( delta < 0 ) {
+ i = resourceNameTable.getItemCount() - 1;
+ }
+ else {
+ i = 0;
+ }
+ }
+ else {
+ i+=delta;
+ }
+
+ if ( i > -1 && i < resourceNameTable.getItemCount() ) {
+ Item item = resourceNameTable.getItem(i);
+ resourceNameTable.select(i);
+ selected = new Object[] { item.getData() };
+ ISelection selection = new SingletonSelection( selected );
+ selectionChanged(new SelectionChangedEvent(viewer, selection));
+ viewer.reveal(selected);
+ }
+ }
+
+ private void setSelected(SelectionChangedEvent event, boolean reveal) {
+ if ( event.getSelection().isEmpty() ) {
+ selected = null;
+ setErrorMessage( "No resource selected" );
+ }
+ else {
+ selected = ((IStructuredSelection) event.getSelection()).toArray();
+ setErrorMessage( null );
+ }
+
+ selectionChanged(event);
+
+ if ( reveal && !event.getSelection().isEmpty() ) {
+ if ( resourceNameCombo != null ) {
+ IStructuredSelection sel = (IStructuredSelection) event.getSelection();
+ resourceNameCombo.select(resourceNameCombo.indexOf((String) sel.getFirstElement()));
+ }
+ else {
+ viewer.setSelection(event.getSelection(), true);
+ }
+ }
+ }
+
+ protected ILabelProvider getLabelProvider() {
+ return labelProvider;
+ }
+
+ public Object[] getSelected() {
+ return selected;
+ }
+
+ public void setSelected(Object[] selected) {
+ this.selected = selected;
+ }
+
+ protected void selectionChanged(SelectionChangedEvent event) {
+ }
+
+ public void setErrorMessage(String errorMessage) {
+ this.errorMessage = errorMessage;
+ if (errorMessageText != null && !errorMessageText.isDisposed()) {
+ errorMessageText.setText(errorMessage == null ? " \n " : errorMessage);
+ boolean hasError = errorMessage != null && (StringConverter.removeWhiteSpaces(errorMessage)).length() > 0;
+ errorMessageText.setEnabled(hasError);
+ errorMessageText.setVisible(hasError);
+ errorMessageText.getParent().update();
+ Control ok = getButton(IDialogConstants.OK_ID);
+ if (ok != null) {
+ ok.setEnabled(!hasError);
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/SigilProjectEditorPart.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/SigilProjectEditorPart.java
new file mode 100644
index 0000000..c1fafaf
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/SigilProjectEditorPart.java
@@ -0,0 +1,299 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.SigilUI;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceChangeEvent;
+import org.eclipse.core.resources.IResourceChangeListener;
+import org.eclipse.core.resources.IResourceDelta;
+import org.eclipse.core.resources.IResourceDeltaVisitor;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.dialogs.ErrorDialog;
+import org.eclipse.jface.dialogs.ProgressMonitorDialog;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IEditorSite;
+import org.eclipse.ui.IFileEditorInput;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.forms.IFormPart;
+import org.eclipse.ui.forms.IManagedForm;
+import org.eclipse.ui.forms.editor.FormEditor;
+import org.eclipse.ui.forms.editor.IFormPage;
+
+public class SigilProjectEditorPart extends FormEditor implements IResourceChangeListener {
+
+ private final Set<IModelElement> unresolvedElements = Collections.synchronizedSet(new HashSet<IModelElement>());
+ private ISigilProjectModel project;
+ private volatile boolean saving = false;
+ private int dependenciesPageIndex;
+
+ // XXX-FIXME-XXX
+ private Image errorImage = null; //SigilUI.imageDescriptorFromPlugin(SigilUI.PLUGIN_ID, "icons/error.gif").createImage();
+ private PropertiesForm textPage;
+
+ public IProject getProject() {
+ IFileEditorInput fileInput = (IFileEditorInput) getEditorInput();
+ return fileInput.getFile().getProject();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.part.EditorPart#doSave(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public void doSave(IProgressMonitor monitor) {
+ monitor.beginTask("Saving", IProgressMonitor.UNKNOWN);
+ try {
+ saving = true;
+ new ProgressMonitorDialog(getSite().getShell()).run(true, true, new IRunnableWithProgress() {
+ public void run(final IProgressMonitor monitor) throws InvocationTargetException,
+ InterruptedException {
+ try {
+ if ( textPage.isDirty() ) {
+ SigilUI.runInUISync(new Runnable() {
+ public void run() {
+ textPage.doSave(monitor);
+ }
+ });
+ project.setBundle(null);
+ }
+ else if ( isDirty() ) {
+ commitPages(true);
+ project.save(monitor);
+ SigilUI.runInUISync(new Runnable() {
+ public void run() {
+ editorDirtyStateChanged();
+ }
+ });
+ }
+
+ monitor.done();
+ } catch (CoreException e) {
+ throw new InvocationTargetException(e);
+ }
+ }
+ });
+ } catch (InvocationTargetException e) {
+ SigilCore.error("Failed to save " + project, e.getTargetException());
+ } catch (InterruptedException e) {
+ monitor.setCanceled(true);
+ return;
+ } finally {
+ saving = false;
+ }
+ monitor.done();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.forms.editor.FormEditor#addPages()
+ */
+ @Override
+ protected void addPages() {
+ try {
+ addPage(new OverviewForm(this, project));
+ addPage(new ContentsForm(this, project));
+ dependenciesPageIndex = addPage(new DependenciesForm(this, project, unresolvedElements));
+ addPage(new ExportsForm(this, project));
+ textPage = new PropertiesForm(this, project);
+ addPage(textPage, getEditorInput());
+ setPartName(project.getSymbolicName());
+
+ refreshTabImages();
+ }
+ catch (PartInitException e) {
+ SigilCore.error( "Failed to build " + this, e );
+ }
+ }
+
+ protected void refreshTabImages() {
+ if(unresolvedElements.isEmpty()) {
+ setPageImage(dependenciesPageIndex, null);
+ } else {
+ setPageImage(dependenciesPageIndex, errorImage);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.part.EditorPart#doSaveAs()
+ */
+ @Override
+ public void doSaveAs() {
+ // save as not allowed
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.part.EditorPart#isSaveAsAllowed()
+ */
+ @Override
+ public boolean isSaveAsAllowed() {
+ return false;
+ }
+
+ @Override
+ public void dispose() {
+ ResourcesPlugin.getWorkspace().removeResourceChangeListener(this);
+ errorImage.dispose();
+ super.dispose();
+ }
+
+ public void resourceChanged(IResourceChangeEvent event) {
+ IResourceDelta delta = event.getDelta();
+ final IFile editorFile = ((IFileEditorInput) getEditorInput()).getFile();
+ try {
+ delta.accept(new IResourceDeltaVisitor() {
+ public boolean visit(IResourceDelta delta) throws CoreException {
+ int kind = delta.getKind();
+ IResource resource = delta.getResource();
+ if(resource instanceof IProject) {
+ if(!editorFile.getProject().equals(resource)) {
+ return false;
+ }
+ if(kind == IResourceDelta.CHANGED && (delta.getFlags() & IResourceDelta.MARKERS) > 0) {
+ loadUnresolvedDependencies();
+ refreshAllPages();
+ }
+ return true;
+ }
+
+ if(resource instanceof IFile) {
+ IFile affectedFile = (IFile) resource;
+ if(affectedFile.equals(editorFile)) {
+ switch (kind) {
+ case IResourceDelta.REMOVED:
+ close(false);
+ break;
+ case IResourceDelta.CHANGED:
+ if(!saving) {
+ reload();
+ }
+ SigilUI.runInUISync( new Runnable() {
+ public void run() {
+ setPartName(project.getSymbolicName());
+ }
+ } );
+ break;
+ }
+ }
+ // Recurse no more
+ return false;
+ }
+
+ return true;
+ }
+ });
+ } catch (CoreException e) {
+ ErrorDialog.openError(getSite().getShell(), "Error", null, e.getStatus());
+ }
+ }
+
+ protected void refreshAllPages() {
+ Runnable op = new Runnable() {
+ public void run() {
+ for(Iterator<?> iter = pages.iterator(); iter.hasNext(); ) {
+ IFormPage page = (IFormPage) iter.next();
+ if(page != null) {
+ IManagedForm managedForm = page.getManagedForm();
+ if(managedForm != null) {
+ managedForm.refresh();
+ IFormPart[] parts = managedForm.getParts();
+ for (IFormPart part : parts) {
+ part.refresh();
+ }
+ }
+ }
+ }
+ firePropertyChange(IEditorPart.PROP_DIRTY);
+ setPartName(project.getSymbolicName());
+ refreshTabImages();
+ }
+ };
+ getSite().getShell().getDisplay().syncExec(op);
+ }
+
+ protected void reload() {
+ project.setBundle(null);
+ refreshAllPages();
+ }
+
+ @Override
+ public void init(IEditorSite site, IEditorInput input) throws PartInitException {
+ super.init(site, input);
+
+ try {
+ this.project = SigilCore.create(getProject());
+ } catch (CoreException e) {
+ throw new PartInitException("Error creating Sigil project", e);
+ }
+
+ ResourcesPlugin.getWorkspace().addResourceChangeListener(this, IResourceChangeEvent.POST_CHANGE);
+
+ if(input instanceof IFileEditorInput) {
+ try {
+ loadUnresolvedDependencies();
+ } catch (CoreException e) {
+ throw new PartInitException("Error retrieving dependency markers", e);
+ }
+ }
+ }
+
+ private void loadUnresolvedDependencies() throws CoreException {
+ ModelElementFactory factory = ModelElementFactory.getInstance();
+ IMarker[] markers = getProject().findMarkers(SigilCore.MARKER_UNRESOLVED_DEPENDENCY, true, IResource.DEPTH_ONE);
+ unresolvedElements.clear();
+
+ for (IMarker marker : markers) {
+ String elementName = (String) marker.getAttribute("element");
+ String versionRangeStr = (String) marker.getAttribute("versionRange");
+ if(elementName != null && versionRangeStr != null) {
+ if(marker.getType().equals(SigilCore.MARKER_UNRESOLVED_IMPORT_PACKAGE)) {
+ IPackageImport pkgImport = factory.newModelElement(IPackageImport.class);
+ pkgImport.setPackageName(elementName);
+ pkgImport.setVersions(VersionRange.parseVersionRange(versionRangeStr));
+ unresolvedElements.add(pkgImport);
+ } else if(marker.getType().equals(SigilCore.MARKER_UNRESOLVED_REQUIRE_BUNDLE)) {
+ IRequiredBundle req = factory.newModelElement(IRequiredBundle.class);
+ req.setSymbolicName(elementName);
+ req.setVersions(VersionRange.parseVersionRange(versionRangeStr));
+ unresolvedElements.add(req);
+ }
+ }
+ }
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/SigilSourcePage.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/SigilSourcePage.java
new file mode 100644
index 0000000..291c927
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/SigilSourcePage.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.ITextInputListener;
+import org.eclipse.jface.text.ITextListener;
+import org.eclipse.jface.text.TextEvent;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.ui.editors.text.TextEditor;
+import org.eclipse.ui.forms.IManagedForm;
+import org.eclipse.ui.forms.editor.FormEditor;
+import org.eclipse.ui.forms.editor.IFormPage;
+import org.eclipse.ui.ide.IDE;
+
+public class SigilSourcePage extends TextEditor implements IFormPage {
+ private final String id;
+ private int index;
+ private SigilProjectEditorPart editor;
+ private boolean active;
+ private Control control;
+
+ public SigilSourcePage(String id) {
+ this.id = id;
+ }
+
+ @Override
+ public void createPartControl(Composite parent) {
+ super.createPartControl(parent);
+ Control[] children = parent.getChildren();
+ control = children[children.length - 1];
+ getSourceViewer().addTextListener( new ITextListener() {
+ public void textChanged(TextEvent event) {
+ if ( editor != null ) {
+ editor.refreshAllPages();
+ }
+ }
+ });
+ //PlatformUI.getWorkbench().getHelpSystem().setHelp(fControl, IHelpContextIds.MANIFEST_SOURCE_PAGE);
+ }
+
+ public void initialize(FormEditor editor) {
+ this.editor = (SigilProjectEditorPart) editor;
+ }
+
+ public FormEditor getEditor() {
+ return editor;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public int getIndex() {
+ return index;
+ }
+
+ public void setIndex(int index) {
+ this.index = index;
+ }
+
+ public boolean isActive() {
+ return active;
+ }
+
+ public void setActive(boolean active) {
+ this.active = active;
+ }
+
+ public Control getPartControl() {
+ return control;
+ }
+
+ public boolean selectReveal(Object object) {
+ if (object instanceof IMarker) {
+ IDE.gotoMarker(this, (IMarker) object);
+ return true;
+ }
+ return false;
+ }
+
+ // static impls
+ public boolean isEditor() {
+ return true;
+ }
+
+ public boolean canLeaveThePage() {
+ return true;
+ }
+
+ public IManagedForm getManagedForm() {
+ // this is not a form
+ return null;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/TestingSection.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/TestingSection.java
new file mode 100644
index 0000000..ca0eb01
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/TestingSection.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.form.SigilPage;
+import org.cauldron.sigil.ui.form.SigilSection;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtensionRegistry;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.debug.ui.ILaunchShortcut;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.forms.events.HyperlinkEvent;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.Hyperlink;
+import org.eclipse.ui.forms.widgets.Section;
+
+public class TestingSection extends SigilSection {
+
+ public TestingSection(SigilPage page, Composite parent, ISigilProjectModel project) throws CoreException {
+ super( page, parent, project );
+ }
+
+ protected void createSection(Section section,FormToolkit toolkit ) {
+ setTitle("Testing");
+
+ Composite body = createTableWrapBody(1, toolkit);
+
+ toolkit.createLabel( body, "Test this project by launching a separate Newton application:" );
+
+ Hyperlink launch = toolkit.createHyperlink( body, "Launch a newton container", SWT.NULL );
+ launch.setHref( "launchShortcut.run.org.cauldron.sigil.launching.shortcut" );
+ launch.addHyperlinkListener(this);
+
+ Hyperlink debug = toolkit.createHyperlink( body, "Debug a newton container", SWT.NULL );
+ debug.setHref( "launchShortcut.debug.org.cauldron.sigil.launching.shortcut" );
+ debug.addHyperlinkListener(this);
+ }
+
+ public void linkActivated(HyperlinkEvent e) {
+ String href = (String) e.getHref();
+ if (href.startsWith("launchShortcut.")) { //$NON-NLS-1$
+ href = href.substring(15);
+ int index = href.indexOf('.');
+ if (index < 0)
+ return; // error. Format of href should be launchShortcut.<mode>.<launchShortcutId>
+ String mode = href.substring(0, index);
+ String id = href.substring(index + 1);
+
+ //getEditor().doSave(null);
+
+ IExtensionRegistry registry = Platform.getExtensionRegistry();
+ IConfigurationElement[] elements = registry.getConfigurationElementsFor("org.eclipse.debug.ui.launchShortcuts"); //$NON-NLS-1$
+ for (int i = 0; i < elements.length; i++) {
+ if (id.equals(elements[i].getAttribute("id"))) //$NON-NLS-1$
+ try {
+ ILaunchShortcut shortcut = (ILaunchShortcut)elements[i].createExecutableExtension("class"); //$NON-NLS-1$
+ // preLaunch();
+ shortcut.launch(new StructuredSelection(getLaunchObject()), mode);
+ } catch (CoreException e1) {
+ e1.printStackTrace();
+ }
+ }
+ }
+ }
+
+ private Object getLaunchObject() {
+ return getProjectModel().getProject();
+ }
+
+ public void linkEntered(HyperlinkEvent e) {
+ }
+
+ public void linkExited(HyperlinkEvent e) {
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ToolsSection.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ToolsSection.java
new file mode 100644
index 0000000..22d9824
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/ToolsSection.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import org.cauldron.sigil.actions.PruneProjectDependenciesAction;
+import org.cauldron.sigil.actions.ResolveProjectDependenciesAction;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.form.SigilPage;
+import org.cauldron.sigil.ui.form.SigilSection;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.forms.events.HyperlinkEvent;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.Hyperlink;
+import org.eclipse.ui.forms.widgets.Section;
+
+public class ToolsSection extends SigilSection {
+
+ public ToolsSection(SigilPage page, Composite parent, ISigilProjectModel project) throws CoreException {
+ super( page, parent, project );
+ }
+
+ protected void createSection(Section section,FormToolkit toolkit ) {
+ setTitle("Tools");
+
+ Composite body = createTableWrapBody(1, toolkit);
+
+ toolkit.createLabel( body, "Tools to help manage this project:" );
+
+ Hyperlink launch = toolkit.createHyperlink( body, "Resolve missing dependencies", SWT.NULL );
+ launch.setHref( "resolve" );
+ launch.addHyperlinkListener(this);
+
+ Hyperlink debug = toolkit.createHyperlink( body, "Prune unused dependencies", SWT.NULL );
+ debug.setHref( "prune" );
+ debug.addHyperlinkListener(this);
+ }
+
+ public void linkActivated(HyperlinkEvent e) {
+ String href = (String) e.getHref();
+ if ( "resolve".equals( href ) ) {
+ handleResolve();
+ }
+ else if ( "prune".equals( href ) ) {
+ handlePrune();
+ }
+ }
+
+ private void handlePrune() {
+ new PruneProjectDependenciesAction(getProjectModel()).run();
+ }
+
+ private void handleResolve() {
+ final ISigilProjectModel project = getProjectModel();
+ new ResolveProjectDependenciesAction(project, true).run();
+ }
+
+ public void linkEntered(HyperlinkEvent e) {
+ }
+
+ public void linkExited(HyperlinkEvent e) {
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/VersionRangeComponent.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/VersionRangeComponent.java
new file mode 100644
index 0000000..7591b38
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/VersionRangeComponent.java
@@ -0,0 +1,251 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.ui.util.IValidationListener;
+import org.eclipse.jface.dialogs.IMessageProvider;
+import org.eclipse.swt.SWT;
+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.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+import org.osgi.framework.Version;
+
+public class VersionRangeComponent extends Composite {
+ private VersionRange versions = VersionRange.ANY_VERSION;
+
+ private Button specificButton;
+
+ private Text specificText;
+ private Button rangeButton;
+ private Text minimumText;
+ private Text maximumText;
+ private Button minInclusiveButton;
+ private Button maxInclusiveButton;
+
+ private Set<VersionsChangeListener> listeners = new HashSet<VersionsChangeListener>();
+ private Set<IValidationListener> validationListeners = new HashSet<IValidationListener>();
+
+ public VersionRangeComponent(Composite parent, int style) {
+ super( parent, style );
+ createComponents(this);
+ }
+
+ public void addVersionChangeListener(VersionsChangeListener listener) {
+ synchronized(listeners) {
+ listeners.add( listener );
+ }
+ }
+
+ public void removeVersionChangeListener(VersionsChangeListener listener) {
+ synchronized(listeners) {
+ listeners.remove( listener );
+ }
+ }
+
+ public void addValidationListener(IValidationListener listener) {
+ validationListeners.add(listener);
+ }
+
+ public void removeValidationListener(IValidationListener listener) {
+ validationListeners.remove(listener);
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ super.setEnabled(enabled);
+ specificButton.setEnabled(enabled);
+ rangeButton.setEnabled(enabled);
+ if ( enabled ) {
+ specificButton.setSelection(versions.isPointVersion());
+ setSpecific();
+ }
+ else {
+ minimumText.setEnabled(enabled);
+ maximumText.setEnabled(enabled);
+ minInclusiveButton.setEnabled(enabled);
+ maxInclusiveButton.setEnabled(enabled);
+ }
+ }
+
+
+ public VersionRange getVersions() {
+ return versions;
+ }
+
+ public void setVersions(VersionRange versions) {
+ this.versions = versions == null ? VersionRange.ANY_VERSION : versions;
+ updateFields();
+ }
+
+ private void updateFields() {
+ if ( versions.isPointVersion() ) {
+ specificButton.setSelection(true);
+ specificText.setText( versions.getCeiling() == VersionRange.INFINITE_VERSION ? "*" : versions.getFloor().toString() );
+ }
+ else {
+ rangeButton.setSelection( true );
+ minimumText.setText( versions.getFloor().toString() );
+ minInclusiveButton.setSelection( !versions.isOpenFloor() );
+ maximumText.setText( versions.getCeiling() == VersionRange.INFINITE_VERSION ? "*" : versions.getCeiling().toString() );
+ maxInclusiveButton.setSelection( !versions.isOpenCeiling() );
+ }
+
+ setSpecific();
+ }
+
+ private void createComponents(Composite body) {
+ setLayout( new GridLayout( 3, false ) );
+
+ specificButton = new Button( body, SWT.RADIO );
+ specificButton.setText( "Specific:" );
+ specificButton.addSelectionListener( new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ setSpecific();
+ }
+ });
+
+ new Label( body, SWT.NONE ).setText("Version:");
+
+ specificText = new Text( body, SWT.BORDER );
+ specificText.addKeyListener( new KeyAdapter() {
+ @Override
+ public void keyReleased(KeyEvent e) {
+ setVersions();
+ }
+ });
+
+ rangeButton = new Button( body, SWT.RADIO );
+ rangeButton.setText( "Range:" );
+
+ new Label(body, SWT.NONE).setText("Minimum:");
+
+ minimumText = new Text( body, SWT.BORDER );
+ minimumText.addKeyListener( new KeyAdapter() {
+ @Override
+ public void keyReleased(KeyEvent e) {
+ setVersions();
+ }
+ });
+
+ minInclusiveButton = new Button( body, SWT.CHECK );
+ minInclusiveButton.setText( "inclusive" );
+ minInclusiveButton.setSelection(true);
+ minInclusiveButton.addSelectionListener( new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ setVersions();
+ }
+ });
+
+ new Label( body, SWT.NONE ).setText("Maximum:");
+ maximumText = new Text( body, SWT.BORDER );
+ maximumText.addKeyListener( new KeyAdapter() {
+ @Override
+ public void keyReleased(KeyEvent e) {
+ setVersions();
+ }
+ });
+
+ maxInclusiveButton = new Button( body, SWT.CHECK );
+ maxInclusiveButton.setText( "inclusive" );
+ maxInclusiveButton.setSelection(false);
+ maxInclusiveButton.addSelectionListener( new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ setVersions();
+ }
+ });
+
+ // Layout
+ specificButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 3, 1));
+ specificText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1));
+ rangeButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 3, 1));
+ minimumText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ maximumText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+
+ updateFields();
+ }
+
+ private void setVersions() {
+ try {
+ if ( specificButton.getSelection() ) {
+ if ( "*".equals( specificText.getText() ) ) {
+ versions = VersionRange.ANY_VERSION;
+ }
+ else if ( specificText.getText().trim().length() == 0 ) {
+ versions = null;
+ }
+ else {
+ Version v = Version.parseVersion( specificText.getText().trim() );
+ versions = new VersionRange( false, v, v, false);
+ }
+ }
+ else {
+ Version min = Version.parseVersion( minimumText.getText() );
+ Version max = "*".equals( maximumText.getText() ) ? VersionRange.INFINITE_VERSION : Version.parseVersion( maximumText.getText() );
+ versions = new VersionRange( !minInclusiveButton.getSelection(), min, max, !maxInclusiveButton.getSelection() );
+ }
+ fireValidationMessage(null, IMessageProvider.NONE);
+ }
+ catch (IllegalArgumentException e) {
+ versions = null;
+ fireValidationMessage("Invalid version", IMessageProvider.ERROR);
+ }
+
+ fireVersionChange();
+ }
+
+ private void fireVersionChange() {
+ synchronized( listeners ) {
+ for ( VersionsChangeListener l : listeners ) {
+ l.versionsChanged(versions);
+ }
+ }
+ }
+
+ private void fireValidationMessage(String message, int level) {
+ for (IValidationListener validationListener : validationListeners) {
+ validationListener.validationMessage(message, level);
+ }
+ }
+
+ private void setSpecific() {
+ boolean specific = specificButton.getSelection();
+ specificButton.setSelection(specific);
+ specificText.setEnabled(specific);
+ minimumText.setEnabled(!specific);
+ maximumText.setEnabled(!specific);
+ minInclusiveButton.setEnabled(!specific);
+ maxInclusiveButton.setEnabled(!specific);
+ setVersions();
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/VersionsChangeListener.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/VersionsChangeListener.java
new file mode 100644
index 0000000..d97bdc2
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/VersionsChangeListener.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import org.cauldron.sigil.model.common.VersionRange;
+
+public interface VersionsChangeListener {
+ void versionsChanged(VersionRange range);
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/WrappedContentProposal.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/WrappedContentProposal.java
new file mode 100644
index 0000000..6f0e397
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/editors/project/WrappedContentProposal.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.editors.project;
+
+import org.eclipse.jface.fieldassist.IContentProposal;
+
+public class WrappedContentProposal<T> implements IContentProposal {
+
+ private final T element;
+ private final IElementDescriptor<? super T> descriptor;
+
+ private WrappedContentProposal(T element, IElementDescriptor<? super T> descriptor) {
+ this.element = element;
+ this.descriptor = descriptor;
+ }
+
+ public static <T> WrappedContentProposal<T> newInstance(T element, IElementDescriptor<? super T> descriptor) {
+ return new WrappedContentProposal<T>(element, descriptor);
+ }
+
+ public String getContent() {
+ return descriptor.getName(element);
+ }
+
+ public int getCursorPosition() {
+ return 0;
+ }
+
+ public String getDescription() {
+ return null;
+ }
+
+ public String getLabel() {
+ return descriptor.getLabel(element);
+ }
+
+ public T getElement() {
+ return element;
+ }
+
+ @Override
+ public String toString() {
+ return getLabel();
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/IFormValueConverter.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/IFormValueConverter.java
new file mode 100644
index 0000000..34c8bb6
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/IFormValueConverter.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.form;
+
+public interface IFormValueConverter {
+ String getLabel(Object value);
+ Object getValue(String label);
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/ISigilFormEntryListener.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/ISigilFormEntryListener.java
new file mode 100644
index 0000000..89afefa
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/ISigilFormEntryListener.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.form;
+
+public interface ISigilFormEntryListener {
+ void browseButtonSelected(SigilFormEntry form);
+ void textValueChanged(SigilFormEntry form);
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/SigilFormEntry.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/SigilFormEntry.java
new file mode 100644
index 0000000..ea5bf9d
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/SigilFormEntry.java
@@ -0,0 +1,173 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.form;
+
+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.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.forms.IFormColors;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+
+public class SigilFormEntry {
+ private static final IFormValueConverter NULL_DESCRIPTOR = new IFormValueConverter() {
+ public String getLabel(Object value) {
+ return (String) value;
+ }
+
+ public Object getValue(String label) {
+ return label;
+ }
+ };
+
+ private Label lbl;
+ private Text txt;
+ private Button btn;
+ private IFormValueConverter descriptor;
+ private boolean freeText = true;
+
+ private Object value;
+ private ISigilFormEntryListener listener;
+
+ public SigilFormEntry(Composite parent, FormToolkit toolkit, String title) {
+ this(parent, toolkit, title, null, null);
+ }
+
+ public SigilFormEntry(Composite parent, FormToolkit toolkit, String title, String browse, IFormValueConverter descriptor) {
+ this.descriptor = descriptor == null ? NULL_DESCRIPTOR : descriptor;
+ createComponent(parent, title, browse, toolkit);
+ }
+
+ public void setFormEntryListener(ISigilFormEntryListener listener) {
+ this.listener = listener;
+ }
+
+ public void setValue(Object value) {
+ this.value = value;
+ String text = descriptor.getLabel(value);
+ if ( text == null ) {
+ text = "";
+ }
+ txt.setText(text);
+ handleValueChanged();
+ }
+
+ public Object getValue() {
+ return value;
+ }
+
+ public void setFreeText(boolean freeText) {
+ this.freeText = freeText;
+ }
+
+ public void setEditable(boolean editable) {
+ lbl.setEnabled(editable);
+ txt.setEditable(editable);
+ if ( btn != null ) {
+ btn.setEnabled(editable);
+ }
+ }
+
+ private void createComponent(Composite parent, String title, String browse, FormToolkit toolkit) {
+ lbl = toolkit.createLabel(parent, title);
+ lbl.setForeground(toolkit.getColors().getColor(IFormColors.TITLE));
+
+ txt = toolkit.createText(parent, "", SWT.SINGLE | SWT.BORDER);
+ txt.addKeyListener( new KeyAdapter() {
+ @Override
+ public void keyPressed(KeyEvent e) {
+ if ( freeText ) {
+ switch ( e.character ) {
+ case '\r': handleValueChanged();
+ }
+ }
+ else {
+ switch ( e.character ) {
+ case '\b':
+ setValue(null);
+ handleValueChanged();
+ default:
+ e.doit = false;
+ break;
+ }
+ }
+ }
+ });
+ txt.addFocusListener( new FocusAdapter() {
+ @Override
+ public void focusLost(FocusEvent e) {
+ handleValueChanged();
+ }
+ });
+
+ if ( browse != null ) {
+ btn = toolkit.createButton(parent, browse, SWT.PUSH);
+ btn.addSelectionListener( new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ handleBrowseSelected();
+ }
+ });
+ }
+
+ fillIntoGrid(parent);
+ }
+
+ private void handleBrowseSelected() {
+ if ( listener != null ) {
+ listener.browseButtonSelected(this);
+ }
+ }
+
+ private void handleValueChanged() {
+ if ( freeText ) {
+ this.value = descriptor.getValue(txt.getText());
+ }
+ if ( listener != null ) {
+ listener.textValueChanged(this);
+ }
+ }
+
+ private void fillIntoGrid(Composite parent) {
+ if ( parent.getLayout() instanceof GridLayout ) {
+ GridLayout layout = (GridLayout) parent.getLayout();
+
+ int cols = layout.numColumns;
+
+ lbl.setLayoutData( new GridData(SWT.LEFT, SWT.CENTER, false, false) );
+
+ if ( btn == null ) {
+ txt.setLayoutData( new GridData(SWT.FILL, SWT.CENTER, true, false, Math.max(1, cols - 1), 1 ) );
+ }
+ else {
+ txt.setLayoutData( new GridData(SWT.FILL, SWT.CENTER, true, false, Math.max(1, cols - 2), 1 ) );
+ }
+ }
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/SigilFormEntryAdapter.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/SigilFormEntryAdapter.java
new file mode 100644
index 0000000..3170d01
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/SigilFormEntryAdapter.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.form;
+
+public class SigilFormEntryAdapter implements ISigilFormEntryListener{
+ public void browseButtonSelected(SigilFormEntry form) {
+ }
+
+ public void textValueChanged(SigilFormEntry form) {
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/SigilPage.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/SigilPage.java
new file mode 100644
index 0000000..a2873ad
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/SigilPage.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.form;
+
+import org.eclipse.ui.forms.editor.FormEditor;
+import org.eclipse.ui.forms.editor.FormPage;
+
+public class SigilPage extends FormPage {
+
+ public SigilPage(FormEditor editor, String id, String title) {
+ super(editor, id, title);
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/SigilSection.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/SigilSection.java
new file mode 100644
index 0000000..b825ec0
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/form/SigilSection.java
@@ -0,0 +1,150 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.form;
+
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.pde.internal.ui.editor.FormLayoutFactory;
+import org.eclipse.pde.internal.ui.parts.FormEntry;
+import org.eclipse.pde.internal.ui.parts.IFormEntryListener;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.IFileEditorInput;
+import org.eclipse.ui.forms.IFormPart;
+import org.eclipse.ui.forms.IPartSelectionListener;
+import org.eclipse.ui.forms.SectionPart;
+import org.eclipse.ui.forms.events.HyperlinkEvent;
+import org.eclipse.ui.forms.widgets.ExpandableComposite;
+import org.eclipse.ui.forms.widgets.FormToolkit;
+import org.eclipse.ui.forms.widgets.Section;
+import org.eclipse.ui.forms.widgets.TableWrapData;
+import org.eclipse.ui.forms.widgets.TableWrapLayout;
+
+@SuppressWarnings("restriction")
+public abstract class SigilSection extends SectionPart implements IFormEntryListener, IPartSelectionListener {
+
+ private SigilPage page;
+ private ISigilProjectModel project;
+
+ public SigilSection(SigilPage page, Composite parent, ISigilProjectModel project) throws CoreException {
+ super(parent, page.getManagedForm().getToolkit(), ExpandableComposite.TITLE_BAR | ExpandableComposite.TWISTIE | ExpandableComposite.EXPANDED );
+ this.project = project;
+ this.page = page;
+ createSection( getSection(), page.getManagedForm().getToolkit() );
+ }
+
+ public ISigilProjectModel getProjectModel() {
+ return project;
+ }
+
+ public SigilPage getPage() {
+ return page;
+ }
+
+ public void setExpanded( boolean expanded ) {
+ getSection().setExpanded(expanded);
+ }
+
+ protected abstract void createSection(Section section,FormToolkit toolkit ) throws CoreException;
+
+ protected void setTitle( String title ) {
+ Section section = getSection();
+ section.setText( title );
+ section.setLayout(FormLayoutFactory.createClearTableWrapLayout(false, 1));
+ TableWrapData data = new TableWrapData(TableWrapData.FILL_GRAB);
+ section.setLayoutData(data);
+ }
+
+ protected void setMarker(String type, String message, int priority, int severity) throws CoreException {
+ IFileEditorInput file = (IFileEditorInput) getPage().getEditor().getEditorInput();
+ IMarker marker = file.getFile().createMarker(type);
+ marker.setAttribute( IMarker.MESSAGE, message );
+ marker.setAttribute(IMarker.PRIORITY, priority);
+ marker.setAttribute( IMarker.SEVERITY, severity );
+ }
+
+ protected void clearMarkers() throws CoreException {
+ IFileEditorInput file = (IFileEditorInput) getPage().getEditor().getEditorInput();
+ file.getFile().deleteMarkers(null, true, IResource.DEPTH_INFINITE );
+ }
+
+ protected Composite createTableWrapBody( int columns, FormToolkit toolkit ) {
+ Section section = getSection();
+ Composite client = toolkit.createComposite(section);
+
+ TableWrapLayout layout = new TableWrapLayout();
+ layout.leftMargin = layout.rightMargin = toolkit.getBorderStyle() != SWT.NULL ? 0 : 2;
+ layout.numColumns = columns;
+ client.setLayout(layout);
+ client.setLayoutData( new TableWrapData( TableWrapData.FILL_GRAB) );
+
+ section.setClient(client);
+
+ return client;
+ }
+
+ protected Composite createGridBody( int columns, boolean columnsSameWidth, FormToolkit toolkit ) {
+ Section section = getSection();
+ Composite client = toolkit.createComposite(section);
+
+ GridLayout layout = new GridLayout();
+
+ layout.makeColumnsEqualWidth = columnsSameWidth;
+ layout.numColumns = columns;
+ client.setLayout(layout);
+
+ client.setLayoutData( new TableWrapData( TableWrapData.FILL_GRAB) );
+
+ section.setClient(client);
+
+ return client;
+ }
+
+ public void browseButtonSelected(FormEntry entry) {
+ }
+
+ public void focusGained(FormEntry entry) {
+ }
+
+ public void selectionChanged(FormEntry entry) {
+ }
+
+ public void textDirty(FormEntry entry) {
+ }
+
+ public void textValueChanged(FormEntry entry) {
+ }
+
+ public void linkActivated(HyperlinkEvent e) {
+ }
+
+ public void linkEntered(HyperlinkEvent e) {
+ }
+
+ public void linkExited(HyperlinkEvent e) {
+ }
+
+ public void selectionChanged(IFormPart part, ISelection selection) {
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/internal/repository/FileSystemRepositoryWizard.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/internal/repository/FileSystemRepositoryWizard.java
new file mode 100644
index 0000000..df5314b
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/internal/repository/FileSystemRepositoryWizard.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.internal.repository;
+
+import org.cauldron.sigil.ui.wizard.repository.RepositoryWizard;
+
+public class FileSystemRepositoryWizard extends RepositoryWizard {
+ @Override
+ public void addPages() {
+ addPage( new FileSystemRepositoryWizardPage(this) );
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/internal/repository/FileSystemRepositoryWizardPage.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/internal/repository/FileSystemRepositoryWizardPage.java
new file mode 100644
index 0000000..a46f7d9
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/internal/repository/FileSystemRepositoryWizardPage.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.internal.repository;
+
+import java.io.File;
+
+import org.cauldron.sigil.ui.wizard.repository.RepositoryWizard;
+import org.cauldron.sigil.ui.wizard.repository.RepositoryWizardPage;
+import org.eclipse.jface.preference.BooleanFieldEditor;
+import org.eclipse.jface.preference.DirectoryFieldEditor;
+import org.eclipse.jface.wizard.IWizardPage;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+
+public class FileSystemRepositoryWizardPage extends RepositoryWizardPage
+ implements IWizardPage {
+
+ private DirectoryFieldEditor dirEditor;
+
+ protected FileSystemRepositoryWizardPage(RepositoryWizard parent) {
+ super("File System Repository", parent);
+ }
+
+ @Override
+ public void createFieldEditors() {
+ dirEditor = new DirectoryFieldEditor("dir", "Directory:", getFieldEditorParent() );
+ dirEditor.getTextControl( getFieldEditorParent() ).addModifyListener( new ModifyListener() {
+ public void modifyText(ModifyEvent e) {
+ checkPageComplete();
+ }
+ } );
+
+ addField( dirEditor );
+ addField(new BooleanFieldEditor("recurse", "Recurse:", getFieldEditorParent() ) );
+ }
+
+ @Override
+ protected void checkPageComplete() {
+ super.checkPageComplete();
+ if ( isPageComplete() ) {
+ setPageComplete(dirEditor.getStringValue().length() > 0);
+ if ( isPageComplete() ) {
+ if ( new File( dirEditor.getStringValue()).isDirectory() ) {
+ setPageComplete(true);
+ setErrorMessage(null);
+ }
+ else {
+ setPageComplete(false);
+ setErrorMessage("Invalid directory");
+ }
+ }
+ }
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/internal/repository/OSGiInstallRepositoryWizard.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/internal/repository/OSGiInstallRepositoryWizard.java
new file mode 100644
index 0000000..ae8da79
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/internal/repository/OSGiInstallRepositoryWizard.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.internal.repository;
+
+import org.cauldron.sigil.ui.wizard.repository.RepositoryWizard;
+
+public class OSGiInstallRepositoryWizard extends RepositoryWizard {
+ @Override
+ public void addPages() {
+ addPage( new OSGiInstallRepositoryWizardPage(this) );
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/internal/repository/OSGiInstallRepositoryWizardPage.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/internal/repository/OSGiInstallRepositoryWizardPage.java
new file mode 100644
index 0000000..772b151
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/internal/repository/OSGiInstallRepositoryWizardPage.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.internal.repository;
+
+import java.util.ArrayList;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.install.IOSGiInstall;
+import org.cauldron.sigil.ui.wizard.repository.RepositoryWizard;
+import org.cauldron.sigil.ui.wizard.repository.RepositoryWizardPage;
+import org.eclipse.jface.preference.RadioGroupFieldEditor;
+
+public class OSGiInstallRepositoryWizardPage extends RepositoryWizardPage {
+
+ protected OSGiInstallRepositoryWizardPage(RepositoryWizard parent) {
+ super("OSGi Install Repository", parent);
+ }
+
+ @Override
+ public void createFieldEditors() {
+ ArrayList<String[]> installs = new ArrayList<String[]>();
+ for ( String id : SigilCore.getInstallManager().getInstallIDs() ) {
+ IOSGiInstall i = SigilCore.getInstallManager().findInstall(id);
+ installs.add( new String[] { i.getType().getName(), id } );
+ }
+ String[][] strs = installs.toArray( new String[installs.size()][] );
+
+ RadioGroupFieldEditor editor = new RadioGroupFieldEditor(
+ "id",
+ "Install",
+ 1, strs, getFieldEditorParent() );
+
+ addField(editor);
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/perspective/SigilPerspectiveFactory.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/perspective/SigilPerspectiveFactory.java
new file mode 100644
index 0000000..34d428c
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/perspective/SigilPerspectiveFactory.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.perspective;
+
+import org.cauldron.sigil.ui.SigilUI;
+import org.eclipse.debug.ui.IDebugUIConstants;
+import org.eclipse.jdt.ui.JavaUI;
+import org.eclipse.ui.IFolderLayout;
+import org.eclipse.ui.IPageLayout;
+import org.eclipse.ui.IPerspectiveFactory;
+import org.eclipse.ui.progress.IProgressConstants;
+
+public class SigilPerspectiveFactory implements IPerspectiveFactory {
+
+ private static final String ID_PROJECT_EXPLORER= "org.eclipse.ui.navigator.ProjectExplorer"; //$NON-NLS-1$
+ private static final String ID_SEARCH_VIEW = "org.eclipse.search.ui.views.SearchView"; //$NON-NLS-1$
+ private static final String ID_CONSOLE_VIEW = "org.eclipse.ui.console.ConsoleView"; //$NON-NLS-1$
+ private static final String ID_TEMPLATES_VIEW = "org.eclipse.ui.texteditor.TemplatesView"; //$NON-NLS-N$
+
+
+ public void createInitialLayout(IPageLayout layout) {
+ /*
+ * Use ProjectExplorer vs PackageExplorer due to a bug with Drag and Drop on Mac OS X which affects PackageExplorer
+ * but not ProjectExplorer. https://bugs.eclipse.org/bugs/show_bug.cgi?id=243529
+ */
+ String editorArea = layout.getEditorArea();
+
+ IFolderLayout folder= layout.createFolder("left", IPageLayout.LEFT, (float)0.25, editorArea); //$NON-NLS-1$
+ folder.addView(ID_PROJECT_EXPLORER);
+ folder.addView(JavaUI.ID_TYPE_HIERARCHY);
+ folder.addPlaceholder(IPageLayout.ID_RES_NAV);
+ folder.addPlaceholder(SigilUI.ID_REPOSITORY_VIEW);
+ folder.addView(IPageLayout.ID_OUTLINE);
+
+ IFolderLayout outputfolder= layout.createFolder("bottom", IPageLayout.BOTTOM, (float)0.75, editorArea); //$NON-NLS-1$
+ outputfolder.addView(IPageLayout.ID_PROBLEM_VIEW);
+ outputfolder.addView(JavaUI.ID_JAVADOC_VIEW);
+ outputfolder.addView(JavaUI.ID_SOURCE_VIEW);
+ outputfolder.addPlaceholder(ID_SEARCH_VIEW);
+ outputfolder.addPlaceholder(ID_CONSOLE_VIEW);
+ outputfolder.addPlaceholder(IPageLayout.ID_BOOKMARKS);
+ outputfolder.addPlaceholder(IProgressConstants.PROGRESS_VIEW_ID);
+ outputfolder.addPlaceholder(SigilUI.ID_DEPENDENCY_VIEW);
+ outputfolder.addPlaceholder(SigilUI.ID_INSTANCES_VIEW);
+
+ layout.addActionSet(IDebugUIConstants.LAUNCH_ACTION_SET);
+ layout.addActionSet(JavaUI.ID_ACTION_SET);
+ layout.addActionSet(JavaUI.ID_ELEMENT_CREATION_ACTION_SET);
+ layout.addActionSet(IPageLayout.ID_NAVIGATE_ACTION_SET);
+
+ // views - sigil
+ layout.addShowViewShortcut(SigilUI.ID_REPOSITORY_VIEW);
+ layout.addShowViewShortcut(SigilUI.ID_DEPENDENCY_VIEW);
+ layout.addShowViewShortcut(SigilUI.ID_INSTANCES_VIEW);
+
+ // views - java
+ layout.addShowViewShortcut(JavaUI.ID_PACKAGES);
+ layout.addShowViewShortcut(JavaUI.ID_TYPE_HIERARCHY);
+ layout.addShowViewShortcut(JavaUI.ID_SOURCE_VIEW);
+ layout.addShowViewShortcut(JavaUI.ID_JAVADOC_VIEW);
+
+ // views - search
+ layout.addShowViewShortcut(ID_SEARCH_VIEW);
+
+ // views - debugging
+ layout.addShowViewShortcut(ID_CONSOLE_VIEW);
+
+ // views - standard workbench
+ layout.addShowViewShortcut(IPageLayout.ID_OUTLINE);
+ layout.addShowViewShortcut(IPageLayout.ID_PROBLEM_VIEW);
+ layout.addShowViewShortcut(IPageLayout.ID_RES_NAV);
+ layout.addShowViewShortcut(IPageLayout.ID_TASK_LIST);
+ layout.addShowViewShortcut(IProgressConstants.PROGRESS_VIEW_ID);
+ layout.addShowViewShortcut(ID_PROJECT_EXPLORER);
+
+ // new actions - Java project creation wizard
+ layout.addNewWizardShortcut("org.eclipse.jdt.ui.wizards.NewPackageCreationWizard"); //$NON-NLS-1$
+ layout.addNewWizardShortcut("org.eclipse.jdt.ui.wizards.NewClassCreationWizard"); //$NON-NLS-1$
+ layout.addNewWizardShortcut("org.eclipse.jdt.ui.wizards.NewInterfaceCreationWizard"); //$NON-NLS-1$
+ layout.addNewWizardShortcut("org.eclipse.jdt.ui.wizards.NewEnumCreationWizard"); //$NON-NLS-1$
+ layout.addNewWizardShortcut("org.eclipse.jdt.ui.wizards.NewAnnotationCreationWizard"); //$NON-NLS-1$
+ layout.addNewWizardShortcut("org.eclipse.jdt.ui.wizards.NewSourceFolderCreationWizard"); //$NON-NLS-1$
+ layout.addNewWizardShortcut("org.eclipse.jdt.ui.wizards.NewSnippetFileCreationWizard"); //$NON-NLS-1$
+ layout.addNewWizardShortcut("org.eclipse.jdt.ui.wizards.NewJavaWorkingSetWizard"); //$NON-NLS-1$
+ layout.addNewWizardShortcut("org.eclipse.ui.wizards.new.folder");//$NON-NLS-1$
+ layout.addNewWizardShortcut("org.eclipse.ui.wizards.new.file");//$NON-NLS-1$
+ layout.addNewWizardShortcut("org.eclipse.ui.editors.wizards.UntitledTextFileWizard");//$NON-NLS-1$
+
+ layout.addNewWizardShortcut("org.cauldron.sigil.editors.newCompositeWizard");
+ layout.addNewWizardShortcut("org.cauldron.sigil.editors.newSystemWizard");
+ layout.addNewWizardShortcut("org.cauldron.sigil.editors.newProjectWizard");
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/ExcludedResourcesPrefsPage.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/ExcludedResourcesPrefsPage.java
new file mode 100644
index 0000000..134f3c3
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/ExcludedResourcesPrefsPage.java
@@ -0,0 +1,169 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.preferences.PrefsUtils;
+import org.eclipse.core.internal.preferences.PrefsMessages;
+import org.eclipse.jface.dialogs.IInputValidator;
+import org.eclipse.jface.dialogs.InputDialog;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.preference.PreferencePage;
+import org.eclipse.jface.viewers.ArrayContentProvider;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPreferencePage;
+
+public class ExcludedResourcesPrefsPage extends PreferencePage implements IWorkbenchPreferencePage {
+
+ private TableViewer viewer;
+ private IWorkbench workbench;
+ private ArrayList<String> resources;
+
+ public ExcludedResourcesPrefsPage() {
+ super();
+ setDescription("Specify resources that should not be offered for inclusion in a generated bundle.");
+ }
+
+ @Override
+ protected Control createContents(Composite parent) {
+ // Create controls
+ Composite composite = new Composite(parent, SWT.NONE);
+ Table table = new Table(composite, SWT.BORDER | SWT.MULTI | SWT.FULL_SELECTION);
+
+ Button btnAdd = new Button(composite, SWT.PUSH);
+ btnAdd.setText("Add");
+
+ final Button btnRemove = new Button(composite, SWT.PUSH);
+ btnRemove.setText("Remove");
+ btnRemove.setEnabled(false);
+
+ // Create viewer
+ viewer = new TableViewer(table);
+ viewer.setContentProvider(new ArrayContentProvider());
+
+ // Load data
+ loadPreferences(false);
+ viewer.setInput(resources);
+
+ // Listeners
+ viewer.addSelectionChangedListener(new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent event) {
+ btnRemove.setEnabled(!viewer.getSelection().isEmpty());
+ }
+ });
+
+ btnRemove.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ IStructuredSelection selection = (IStructuredSelection) viewer.getSelection();
+ Object[] deleted = selection.toArray();
+ for(Object delete : deleted) {
+ resources.remove(delete);
+ }
+ viewer.remove(deleted);
+ }
+ });
+
+ btnAdd.addSelectionListener(new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ InputDialog dialog = new InputDialog(getShell(), "Add Resource", "Enter resource name", "", new IInputValidator() {
+ public String isValid(String newText) {
+ String error = null;
+ if(newText == null || newText.length() == 0) {
+ error = "Name must not be empty.";
+ } else if(resources.contains(newText)) {
+ error = "Specified resource name is already on the list.";
+ }
+ return error;
+ }
+ });
+
+ if(dialog.open() == Window.OK) {
+ String value = dialog.getValue();
+ resources.add(value);
+ viewer.add(value);
+ }
+ }
+ });
+
+ // Layout
+ composite.setLayout(new GridLayout(2, false));
+ table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 3));
+ btnAdd.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+ btnRemove.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+
+ return composite;
+ }
+
+ private void loadPreferences(boolean useDefaults) {
+ String resourcesListStr = useDefaults
+ ? getPreferenceStore().getDefaultString(SigilCore.DEFAULT_EXCLUDED_RESOURCES)
+ : getPreferenceStore().getString(SigilCore.DEFAULT_EXCLUDED_RESOURCES);
+ String[] resourcesArray = PrefsUtils.stringToArray(resourcesListStr);
+
+ resources = new ArrayList<String>(resourcesArray.length);
+ for (String resource : resourcesArray) {
+ resources.add(resource);
+ }
+ }
+
+ public void init(IWorkbench workbench) {
+ this.workbench = workbench;
+ }
+
+ @Override
+ protected IPreferenceStore doGetPreferenceStore() {
+ return SigilCore.getDefault().getPreferenceStore();
+ }
+
+ @Override
+ public boolean performOk() {
+ String resourcesStr = PrefsUtils.arrayToString(resources.toArray(new String[resources.size()]));
+ getPreferenceStore().setValue(SigilCore.DEFAULT_EXCLUDED_RESOURCES, resourcesStr);
+ return true;
+ }
+
+ @Override
+ protected void performDefaults() {
+ super.performDefaults();
+ loadPreferences(true);
+ viewer.setInput(resources);
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/LibraryConfigurationDialog.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/LibraryConfigurationDialog.java
new file mode 100644
index 0000000..d7b6786
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/LibraryConfigurationDialog.java
@@ -0,0 +1,300 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences;
+
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.TreeSet;
+
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.eclipse.ILibrary;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IPackageModelElement;
+import org.cauldron.sigil.ui.editors.project.NewResourceSelectionDialog;
+import org.cauldron.sigil.ui.util.DefaultTableProvider;
+import org.cauldron.sigil.ui.util.ResourcesDialogHelper;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.IMessageProvider;
+import org.eclipse.jface.dialogs.TitleAreaDialog;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.SWT;
+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.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+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.Table;
+import org.eclipse.swt.widgets.Text;
+import org.osgi.framework.Version;
+
+public class LibraryConfigurationDialog extends TitleAreaDialog {
+
+ private static final Comparator<IPackageImport> COMPARATOR = new Comparator<IPackageImport>() {
+ public int compare(IPackageImport o1, IPackageImport o2) {
+ return o1.getPackageName().compareTo(o2.getPackageName());
+ }
+ };
+
+ private String name;
+ private Version version;
+ private TreeSet<IPackageImport> packageImports = new TreeSet<IPackageImport>(COMPARATOR);
+
+ private boolean editOnly;
+
+ private TableViewer viewer;
+ private Text txtName;
+ private Text txtVersion;
+
+ public LibraryConfigurationDialog(Shell parentShell) {
+ super(parentShell);
+ name = "";
+ version = Version.emptyVersion;
+ }
+
+ public LibraryConfigurationDialog(Shell parentShell, ILibrary lib) {
+ super(parentShell);
+ editOnly = true;
+ name = lib.getName();
+ version = lib.getVersion();
+ packageImports.addAll( lib.getImports() );
+ }
+
+ @Override
+ protected Control createDialogArea(Composite par) {
+ setTitle("Add Library");
+ Composite container = (Composite) super.createDialogArea(par);
+
+ Composite topPanel = new Composite(container, SWT.NONE);
+
+ new Label(topPanel, SWT.NONE).setText("Name");
+
+ txtName = new Text(topPanel, SWT.BORDER);
+ txtName.setEditable( !editOnly );
+ if(name != null) txtName.setText(name);
+
+ new Label(topPanel, SWT.NONE).setText("Version");
+
+ txtVersion = new Text(topPanel, SWT.BORDER);
+ txtVersion.setText( version.toString() );
+ txtVersion.setEditable( !editOnly );
+
+ Composite bottomPanel = new Composite(container, SWT.NONE);
+
+ Table table = new Table(bottomPanel, SWT.BORDER);
+ table.setSize( new Point( 300, 200 ) );
+
+ Button add = new Button(bottomPanel, SWT.PUSH);
+ add.setText("Add...");
+
+ final Button edit = new Button(bottomPanel, SWT.PUSH);
+ edit.setText("Edit...");
+ edit.setEnabled(false);
+
+ final Button remove = new Button(bottomPanel, SWT.PUSH);
+ remove.setText("Remove");
+ remove.setEnabled(false);
+
+ updateState();
+
+ // Hookup Listeners
+ txtName.addModifyListener(new ModifyListener() {
+ public void modifyText(ModifyEvent e) {
+ updateState();
+ }
+ });
+ txtVersion.addModifyListener(new ModifyListener() {
+ public void modifyText(ModifyEvent e) {
+ updateState();
+ }
+ });
+ add.addSelectionListener( new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ handleAdd();
+ }
+ });
+ edit.addSelectionListener( new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ handleEdit();
+ }
+ });
+ remove.addSelectionListener( new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ handleRemove();
+ }
+ });
+
+ // Layout
+ topPanel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ topPanel.setLayout(new GridLayout(2, false));
+ txtName.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ txtVersion.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+
+ bottomPanel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ bottomPanel.setLayout(new GridLayout(2, false));
+ table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 4));
+ add.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+ edit.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+ remove.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+
+ // Table Viewer
+ viewer = new TableViewer(table);
+ viewer.setLabelProvider(new LabelProvider() {
+ @Override
+ public String getText(Object element) {
+ IPackageImport pi = (IPackageImport) element;
+ return pi.getPackageName() + " " + pi.getVersions();
+ }
+ });
+ viewer.addSelectionChangedListener( new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent event) {
+ edit.setEnabled(!event.getSelection().isEmpty());
+ remove.setEnabled(!event.getSelection().isEmpty());
+ }
+ });
+ viewer.setContentProvider( new DefaultTableProvider() {
+ public Object[] getElements(Object inputElement) {
+ return toArray(inputElement);
+ }
+ } );
+
+ viewer.setInput(packageImports);
+ return container;
+ }
+
+ private void updateState() {
+ String error = null;
+ String warning = null;
+
+ name = txtName.getText();
+
+ try {
+ version = new Version(txtVersion.getText());
+ if(version.getQualifier().indexOf('_') > -1) {
+ warning = "The use of underscores in a version qualifier is discouraged.";
+ }
+ } catch (IllegalArgumentException e) {
+ version = null;
+ error = "Invalid version format";
+ }
+
+ Button okButton = getButton(IDialogConstants.OK_ID);
+ if(okButton != null && !okButton.isDisposed()) okButton.setEnabled(allowOkay());
+
+ setErrorMessage(error);
+ setMessage(warning, IMessageProvider.WARNING);
+ }
+
+ private boolean allowOkay() {
+ return name != null && name.length() > 0 && version != null;
+ }
+
+ @Override
+ protected Button createButton(Composite parent, int id, String label, boolean defaultButton) {
+ Button button = super.createButton(parent, id, label, defaultButton);
+ if(id == IDialogConstants.OK_ID) {
+ button.setEnabled(allowOkay());
+ }
+ return button;
+ }
+
+ private void handleAdd() {
+ /*NewResourceSelectionDialog<? extends IPackageModelElement> dialog = ResourcesDialogHelper.createImportDialog(getShell(), "Add Imported Package", null, packageImports);
+ if ( dialog.open() == Window.OK ) {
+ IPackageImport pi = ModelElementFactory.getInstance().newModelElement(IPackageImport.class);
+ pi.setPackageName(dialog.getSelectedName());
+ pi.setVersions(dialog.getSelectedVersions());
+ pi.setOptional(dialog.isOptional());
+
+ packageImports.add(pi);
+ viewer.refresh();
+ }*/
+ }
+
+ private void handleEdit() {
+ /*IStructuredSelection selection = (IStructuredSelection) viewer.getSelection();
+
+ boolean changed = false;
+
+ if ( !selection.isEmpty() ) {
+ for ( Iterator<IPackageImport> i = selection.iterator(); i.hasNext(); ) {
+ IPackageImport packageImport = i.next();
+ NewResourceSelectionDialog<? extends IPackageModelElement> dialog = ResourcesDialogHelper.createImportDialog( getShell(), "Edit Imported Package", packageImport, packageImports );
+ dialog.setVersions( packageImport.getVersions() );
+ dialog.setOptional(packageImport.isOptional());
+ if ( dialog.open() == Window.OK ) {
+ changed = true;
+ String packageName = dialog.getSelectedName();
+ VersionRange versionRange = dialog.getSelectedVersions();
+
+ IPackageImport newImport = ModelElementFactory.getInstance().newModelElement(IPackageImport.class);
+ newImport.setPackageName(packageName);
+ newImport.setVersions(versionRange);
+ newImport.setOptional(dialog.isOptional());
+
+ packageImports.remove(packageImport);
+ packageImports.add(newImport);
+ }
+ }
+ }
+
+ if ( changed ) {
+ viewer.refresh();
+ } */
+ }
+
+ private void handleRemove() {
+ IStructuredSelection selection = (IStructuredSelection) viewer.getSelection();
+
+ if ( !selection.isEmpty() ) {
+ for ( Iterator<IPackageImport> i = selection.iterator(); i.hasNext(); ) {
+ packageImports.remove( i.next() );
+ }
+
+ viewer.refresh();
+ }
+ }
+
+ public ILibrary getLibrary() {
+ ILibrary library = ModelElementFactory.getInstance().newModelElement(ILibrary.class);
+
+ library.setName(name);
+ library.setVersion(version);
+
+ for (IPackageImport pi : packageImports) {
+ library.addImport(pi);
+ }
+
+ return library;
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/LibraryPreferencePage.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/LibraryPreferencePage.java
new file mode 100644
index 0000000..cf77287
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/LibraryPreferencePage.java
@@ -0,0 +1,243 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences;
+
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.TreeSet;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.eclipse.ILibrary;
+import org.cauldron.sigil.ui.util.DefaultTableProvider;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.preference.PreferencePage;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+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.Table;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPreferencePage;
+
+public class LibraryPreferencePage extends PreferencePage implements
+ IWorkbenchPreferencePage {
+
+ private TreeSet<ILibrary> libraries;
+ private TableViewer libraryView;
+
+ private Table table;
+ private Button btnAdd;
+ private Button btnEdit;
+ private Button btnRemove;
+
+ public void init(IWorkbench workbench) {
+ }
+
+ @Override
+ protected Control createContents(Composite parent) {
+ Control control = initContents( parent );
+ loadPreferences();
+ return control;
+ }
+
+ @Override
+ protected IPreferenceStore doGetPreferenceStore() {
+ return SigilCore.getDefault().getPreferenceStore();
+ }
+
+ @Override
+ protected void performDefaults() {
+ super.performDefaults();
+ }
+
+ @Override
+ public boolean performOk() {
+ IPreferenceStore prefs = getPreferenceStore();
+ for ( String key : prefs.getString( SigilCore.LIBRARY_KEYS_PREF ).split( "," ) ) {
+ prefs.setToDefault(key);
+ }
+
+ StringBuffer keys = new StringBuffer();
+
+ for ( ILibrary lib : libraries ) {
+ throw new IllegalStateException( "XXX-FIXME-XXX");
+ }
+
+ prefs.setValue( SigilCore.LIBRARY_KEYS_PREF, keys.toString() );
+
+ return true;
+ }
+
+ private Control initContents(Composite parent) {
+ Composite control = new Composite(parent, SWT.NONE);
+ control.setFont(parent.getFont());
+
+ GridLayout grid = new GridLayout(3, false);
+ control.setLayout(grid);
+
+ initRepositories(control);
+
+ return control;
+ }
+
+ private void initRepositories(Composite composite) {
+ // Create controls
+ new Label(composite, SWT.NONE).setText("Libraries:");
+ new Label(composite, SWT.NONE); // Spacer
+ table = new Table(composite, SWT.SINGLE | SWT.BORDER );
+ //table.setFont(control.getFont());
+ btnAdd = new Button(composite, SWT.PUSH);
+ btnAdd.setText("Add...");
+ //add.setFont(control.getFont());
+ btnEdit = new Button(composite, SWT.PUSH);
+ btnEdit.setText("Edit...");
+ //edit.setFont(control.getFont());
+ btnRemove = new Button(composite, SWT.PUSH);
+ btnRemove.setText("Remove");
+ //remove.setFont(control.getFont());
+
+ // Table Model
+ libraries = new TreeSet<ILibrary>(new Comparator<ILibrary> () {
+ public int compare(ILibrary l1, ILibrary l2) {
+ int c = l1.getName().compareTo( l2.getName() );
+ if ( c == 0 ) {
+ c = l1.getVersion().compareTo( l2.getVersion() );
+ }
+ return c;
+ }
+ });
+ libraryView = new TableViewer(table);
+ libraryView.setLabelProvider(new LabelProvider() {
+ public String getText(Object element) {
+ ILibrary rep = (ILibrary) element;
+ return rep.getName() + " " + rep.getVersion();
+ }
+ });
+ libraryView.setContentProvider( new DefaultTableProvider() {
+ public Object[] getElements(Object inputElement) {
+ return toArray(inputElement);
+ }
+ } );
+ libraryView.setInput(libraries);
+
+ // Initialize controls
+ updateButtonStates();
+
+ // Hookup Listeners
+ libraryView.addSelectionChangedListener( new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent event) {
+ updateButtonStates();
+ }
+ });
+ btnAdd.addSelectionListener( new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ handleAdd();
+ }
+ });
+ btnEdit.addSelectionListener( new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ handleEdit();
+ }
+ });
+ btnRemove.addSelectionListener( new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ handleRemove();
+ }
+ });
+
+ // Layout
+ composite.setLayout(new GridLayout(2, false));
+ table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 4));
+ GridDataFactory buttonGD = GridDataFactory.swtDefaults().align(SWT.FILL, SWT.CENTER);
+ btnAdd.setLayoutData(buttonGD.create());
+ btnEdit.setLayoutData(buttonGD.create());
+ btnRemove.setLayoutData(buttonGD.create());
+ }
+
+ private void updateButtonStates() {
+ ISelection sel = libraryView.getSelection();
+ btnEdit.setEnabled(!sel.isEmpty());
+ btnRemove.setEnabled(!sel.isEmpty());
+ }
+
+
+ private void handleAdd() {
+ LibraryConfigurationDialog d = new LibraryConfigurationDialog(getShell());
+ if ( d.open() == Window.OK ) {
+ libraries.add(d.getLibrary());
+ libraryView.refresh();
+ }
+ }
+
+ private void handleEdit() {
+ IStructuredSelection sel = (IStructuredSelection) libraryView.getSelection();
+ boolean change = false;
+
+
+ for ( @SuppressWarnings("unchecked") Iterator<ILibrary> i = sel.iterator(); i.hasNext(); ) {
+ ILibrary lib = i.next();
+ LibraryConfigurationDialog d = new LibraryConfigurationDialog(getShell(), lib );
+ if ( d.open() == Window.OK ) {
+ libraries.remove(lib);
+ libraries.add(d.getLibrary());
+ change = true;
+ }
+ }
+
+ if ( change ) {
+ libraryView.refresh();
+ }
+ }
+
+ private void handleRemove() {
+ IStructuredSelection sel = (IStructuredSelection) libraryView.getSelection();
+ for ( @SuppressWarnings("unchecked") Iterator<ILibrary> i = sel.iterator(); i.hasNext(); ) {
+ libraries.remove(i);
+ }
+ libraryView.refresh();
+ }
+
+ private void loadPreferences() {
+ IPreferenceStore prefs = getPreferenceStore();
+ String keys = prefs.getString( SigilCore.LIBRARY_KEYS_PREF );
+ if ( keys.trim().length() > 0 ) {
+ for ( String key : keys.split( "," ) ) {
+ String libStr = prefs.getString(key);
+ // XXX-FIXME-XXX parse library string
+ // lib = parse(libstr);
+ // libraries.add(lib);
+ }
+ libraryView.refresh();
+ }
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/OptionalPrompt.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/OptionalPrompt.java
new file mode 100644
index 0000000..45ace9f
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/OptionalPrompt.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences;
+
+import org.cauldron.sigil.preferences.PromptablePreference;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.MessageDialogWithToggle;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.swt.widgets.Shell;
+
+public class OptionalPrompt {
+ public static boolean optionallyPrompt(IPreferenceStore prefStore, String prefName, String title, String text, Shell parentShell) {
+ boolean result = false;
+
+ PromptablePreference value = PromptablePreference.valueOf(prefStore.getString(prefName));
+ switch(value) {
+ case Always:
+ result = true;
+ break;
+ case Never:
+ result = false;
+ break;
+ case Prompt:
+ MessageDialogWithToggle dialog = MessageDialogWithToggle.openYesNoQuestion(parentShell, title, text, "Do not ask this again", false, null, null);
+ result = (dialog.getReturnCode() == IDialogConstants.YES_ID);
+ if(dialog.getToggleState()) {
+ // User said don't ask again... take the current answer as the new preference
+ prefStore.setValue(prefName, result ? PromptablePreference.Always.name() : PromptablePreference.Never.name());
+ }
+ }
+
+ return result;
+ }
+
+ public static int optionallyPromptWithCancel(IPreferenceStore prefStore, String prefName, String title, String text, Shell parentShell) {
+ int result = IDialogConstants.NO_ID;
+
+ PromptablePreference value = PromptablePreference.valueOf(prefStore.getString(prefName));
+ switch(value) {
+ case Always:
+ result = IDialogConstants.YES_ID;
+ break;
+ case Never:
+ result = IDialogConstants.NO_ID;
+ break;
+ case Prompt:
+ MessageDialogWithToggle dialog = MessageDialogWithToggle.openYesNoCancelQuestion(parentShell, title, text, "Do not ask this again", false, null, null);
+ result = dialog.getReturnCode();
+ if ( result != IDialogConstants.CANCEL_ID ) {
+ if(dialog.getToggleState()) {
+ // User said don't ask again... take the current answer as the new preference
+ prefStore.setValue(prefName, (result == IDialogConstants.YES_ID) ? PromptablePreference.Always.name() : PromptablePreference.Never.name());
+ }
+ }
+ }
+
+ return result;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/ProjectDependentPreferencesPage.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/ProjectDependentPreferencesPage.java
new file mode 100644
index 0000000..07a564c
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/ProjectDependentPreferencesPage.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences;
+
+import org.cauldron.sigil.ui.util.ProjectUtils;
+import org.eclipse.jface.preference.PreferencePage;
+
+public abstract class ProjectDependentPreferencesPage extends PreferencePage {
+
+ public ProjectDependentPreferencesPage(String title) {
+ super(title);
+ }
+
+ @Override
+ public boolean performOk() {
+ if ( isDirty() ) {
+ return ProjectUtils.runTaskWithRebuildCheck(new Runnable() {
+ public void run() {
+ doSave();
+ }
+
+ }, getShell());
+ }
+ else {
+ return true;
+ }
+ }
+
+ protected abstract void doSave();
+
+ protected abstract boolean isDirty();
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/SigilPreferencePage.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/SigilPreferencePage.java
new file mode 100644
index 0000000..4a16093
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/SigilPreferencePage.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.preferences.PromptablePreference;
+import org.eclipse.jface.preference.FieldEditorPreferencePage;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.preference.RadioGroupFieldEditor;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPreferencePage;
+
+public class SigilPreferencePage extends FieldEditorPreferencePage implements IWorkbenchPreferencePage {
+
+ @Override
+ protected void createFieldEditors() {
+ RadioGroupFieldEditor impExpField = new RadioGroupFieldEditor(SigilCore.PREFERENCES_ADD_IMPORT_FOR_EXPORT, "Add Imports for New Exports", 1, new String[][] {
+ new String[] { "Always (Recommended)", PromptablePreference.Always.toString() },
+ new String[] { "Prompt", PromptablePreference.Prompt.toString() },
+ new String[] { "Never", PromptablePreference.Never.toString() }
+ }, getFieldEditorParent(), true);
+
+ addField(impExpField);
+
+ RadioGroupFieldEditor rebuildExpField = new RadioGroupFieldEditor(SigilCore.PREFERENCES_REBUILD_PROJECTS, "Rebuild Projects On Install Change", 1, new String[][] {
+ new String[] { "Always (Recommended)", PromptablePreference.Always.toString() },
+ new String[] { "Prompt", PromptablePreference.Prompt.toString() },
+ new String[] { "Never", PromptablePreference.Never.toString() }
+ }, getFieldEditorParent(), true);
+
+ addField(rebuildExpField);
+ }
+
+ @Override
+ protected IPreferenceStore doGetPreferenceStore() {
+ return SigilCore.getDefault().getPreferenceStore();
+ }
+
+ public void init(IWorkbench workbench) {
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/VersionsPreferencePage.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/VersionsPreferencePage.java
new file mode 100644
index 0000000..82cd79c
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/VersionsPreferencePage.java
@@ -0,0 +1,368 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.common.VersionRangeBoundingRule;
+import org.cauldron.sigil.ui.SigilUI;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.preference.PreferencePage;
+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.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPreferencePage;
+import org.osgi.framework.Version;
+
+public class VersionsPreferencePage extends PreferencePage implements IWorkbenchPreferencePage {
+
+ private static final Version SAMPLE_VERSION = new Version(1, 2, 3, "qualifier");
+
+ private IWorkbench workbench;
+
+ private Button btnLowerBoundExact;
+ private Button btnLowerBoundMicro;
+ private Button btnLowerBoundMinor;
+ private Button btnLowerBoundMajor;
+ private Button btnLowerBoundAny;
+
+ private Button btnUpperBoundExact;
+ private Button btnUpperBoundMicro;
+ private Button btnUpperBoundMinor;
+ private Button btnUpperBoundMajor;
+ private Button btnUpperBoundAny;
+
+ private Text txtSampleVersion;
+ private Label lblCalculatedRange;
+ private Text txtMatchVersion;
+ private Label lblMatchResult;
+
+ private VersionRangeBoundingRule lowerBoundRule;
+ private VersionRangeBoundingRule upperBoundRule;
+
+ private Version sampleVersion = SAMPLE_VERSION;
+ private Version matchVersion = null;
+
+ public VersionsPreferencePage() {
+ super();
+ setDescription("Specify the Lower and Upper bounds for a default version range calculated from a point version, e.g. \"1.2.3.qualifier\"");
+ }
+
+ @Override
+ protected Control createContents(Composite parent) {
+ // Create controls
+ Composite composite = new Composite(parent, SWT.NONE);
+
+ Group grpLowerBound = new Group(composite, SWT.NONE);
+ grpLowerBound.setText("Lower Bound");
+ btnLowerBoundExact = new Button(grpLowerBound, SWT.RADIO);
+ btnLowerBoundExact.setText("Exact e.g. [1.2.3.qualifer, ...)");
+ btnLowerBoundMicro = new Button(grpLowerBound, SWT.RADIO);
+ btnLowerBoundMicro.setText("Micro e.g. [1.2.3, ...)");
+ btnLowerBoundMinor = new Button(grpLowerBound, SWT.RADIO);
+ btnLowerBoundMinor.setText("Minor e.g. [1.2, ...)");
+ btnLowerBoundMajor = new Button(grpLowerBound, SWT.RADIO);
+ btnLowerBoundMajor.setText("Major e.g. [1, ...)");
+ btnLowerBoundAny = new Button(grpLowerBound, SWT.RADIO);
+ btnLowerBoundAny.setText("Any e.g. [0, ...)");
+
+ Group grpUpperBound = new Group(composite, SWT.NONE);
+ grpUpperBound.setText("Upper Bound");
+
+ btnUpperBoundExact = new Button(grpUpperBound, SWT.RADIO);
+ btnUpperBoundExact.setText("Exact e.g. [..., 1.2.3.qualifer]");
+ btnUpperBoundMicro = new Button(grpUpperBound, SWT.RADIO);
+ btnUpperBoundMicro.setText("Micro e.g. [..., 1.2.4)");
+ btnUpperBoundMinor = new Button(grpUpperBound, SWT.RADIO);
+ btnUpperBoundMinor.setText("Minor e.g. [..., 1.3)");
+ btnUpperBoundMajor = new Button(grpUpperBound, SWT.RADIO);
+ btnUpperBoundMajor.setText("Major e.g. [..., 2)");
+ btnUpperBoundAny = new Button(grpUpperBound, SWT.RADIO);
+ btnUpperBoundAny.setText("Any e.g. [..., \u221e)");
+
+ Group grpRangeTest = new Group(composite, SWT.NONE);
+ grpRangeTest.setText("Range Test");
+ new Label(grpRangeTest, SWT.NONE).setText("Sample Input Version: ");
+ txtSampleVersion = new Text(grpRangeTest, SWT.BORDER);
+ new Label(grpRangeTest, SWT.NONE).setText("Calculated Version Range: ");
+ lblCalculatedRange = new Label(grpRangeTest, SWT.NONE);
+
+ new Label(grpRangeTest, SWT.NONE).setText("Test: ");
+ txtMatchVersion = new Text(grpRangeTest, SWT.BORDER);
+ new Label(grpRangeTest, SWT.NONE).setText("Result: ");
+ lblMatchResult = new Label(grpRangeTest, SWT.NONE);
+
+ // Initialize controls
+ loadPreferences(false);
+ updateRadioButtons();
+
+ txtSampleVersion.setText(sampleVersion.toString());
+ updateCalculatedRange();
+
+ // Add listeners
+ SelectionListener buttonListener = new SelectionListener() {
+ public void widgetSelected(SelectionEvent e) {
+ readRadioButtons();
+ updateCalculatedRange();
+ }
+
+ public void widgetDefaultSelected(SelectionEvent e) {
+ readRadioButtons();
+ updateCalculatedRange();
+ }
+ };
+ btnLowerBoundAny.addSelectionListener(buttonListener);
+ btnLowerBoundMajor.addSelectionListener(buttonListener);
+ btnLowerBoundMinor.addSelectionListener(buttonListener);
+ btnLowerBoundMicro.addSelectionListener(buttonListener);
+ btnLowerBoundExact.addSelectionListener(buttonListener);
+
+ btnUpperBoundAny.addSelectionListener(buttonListener);
+ btnUpperBoundMajor.addSelectionListener(buttonListener);
+ btnUpperBoundMinor.addSelectionListener(buttonListener);
+ btnUpperBoundMicro.addSelectionListener(buttonListener);
+ btnUpperBoundExact.addSelectionListener(buttonListener);
+
+ txtSampleVersion.addModifyListener(new ModifyListener() {
+ public void modifyText(ModifyEvent e) {
+ try {
+ sampleVersion = new Version(txtSampleVersion.getText());
+ } catch (IllegalArgumentException x) {
+ sampleVersion = null;
+ }
+ updateCalculatedRange();
+ }
+ });
+ txtMatchVersion.addModifyListener(new ModifyListener() {
+ public void modifyText(ModifyEvent e) {
+ try {
+ matchVersion = new Version(txtMatchVersion.getText());
+ } catch (IllegalArgumentException x) {
+ matchVersion = null;
+ }
+ updateCalculatedRange();
+ }
+ });
+
+ // Layout
+ GridLayout layout = new GridLayout(1, false);
+ layout.verticalSpacing = 20;
+ composite.setLayout(layout);
+
+ grpLowerBound.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+ grpLowerBound.setLayout(new GridLayout(1, false));
+
+ grpUpperBound.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+ grpUpperBound.setLayout(new GridLayout(1, false));
+
+ grpRangeTest.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, true));
+ grpRangeTest.setLayout(new GridLayout(2, false));
+
+ txtSampleVersion.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ lblCalculatedRange.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ txtMatchVersion.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ lblMatchResult.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+
+ return composite;
+ }
+
+ private void loadPreferences(boolean useDefaults) {
+ IPreferenceStore prefs = getPreferenceStore();
+ String lowerBoundStr;
+ if(useDefaults) {
+ lowerBoundStr = prefs.getDefaultString(SigilCore.DEFAULT_VERSION_LOWER_BOUND);
+ } else {
+ lowerBoundStr = prefs.getString(SigilCore.DEFAULT_VERSION_LOWER_BOUND);
+ }
+
+ String upperBoundStr;
+ if(useDefaults) {
+ upperBoundStr = prefs.getDefaultString(SigilCore.DEFAULT_VERSION_UPPER_BOUND);
+ } else {
+ upperBoundStr = prefs.getString(SigilCore.DEFAULT_VERSION_UPPER_BOUND);
+ }
+
+ lowerBoundRule = VersionRangeBoundingRule.valueOf(lowerBoundStr);
+ upperBoundRule = VersionRangeBoundingRule.valueOf(upperBoundStr);
+ }
+
+ private void updateRadioButtons() {
+ switch (lowerBoundRule) {
+ case Exact:
+ btnLowerBoundExact.setSelection(true);
+ btnLowerBoundMicro.setSelection(false);
+ btnLowerBoundMinor.setSelection(false);
+ btnLowerBoundMajor.setSelection(false);
+ btnLowerBoundAny.setSelection(false);
+ break;
+ case Micro:
+ btnLowerBoundExact.setSelection(false);
+ btnLowerBoundMicro.setSelection(true);
+ btnLowerBoundMinor.setSelection(false);
+ btnLowerBoundMajor.setSelection(false);
+ btnLowerBoundAny.setSelection(false);
+ break;
+ case Minor:
+ btnLowerBoundExact.setSelection(false);
+ btnLowerBoundMicro.setSelection(false);
+ btnLowerBoundMinor.setSelection(true);
+ btnLowerBoundMajor.setSelection(false);
+ btnLowerBoundAny.setSelection(false);
+ break;
+ case Major:
+ btnLowerBoundExact.setSelection(false);
+ btnLowerBoundMicro.setSelection(false);
+ btnLowerBoundMinor.setSelection(false);
+ btnLowerBoundMajor.setSelection(true);
+ btnLowerBoundAny.setSelection(false);
+ break;
+ case Any:
+ btnLowerBoundExact.setSelection(false);
+ btnLowerBoundMicro.setSelection(false);
+ btnLowerBoundMinor.setSelection(false);
+ btnLowerBoundMajor.setSelection(false);
+ btnLowerBoundAny.setSelection(true);
+ break;
+ }
+
+ switch (upperBoundRule) {
+ case Exact:
+ btnUpperBoundExact.setSelection(true);
+ btnUpperBoundMicro.setSelection(false);
+ btnUpperBoundMinor.setSelection(false);
+ btnUpperBoundMajor.setSelection(false);
+ btnUpperBoundAny.setSelection(false);
+ break;
+ case Micro:
+ btnUpperBoundExact.setSelection(false);
+ btnUpperBoundMicro.setSelection(true);
+ btnUpperBoundMinor.setSelection(false);
+ btnUpperBoundMajor.setSelection(false);
+ btnUpperBoundAny.setSelection(false);
+ break;
+ case Minor:
+ btnUpperBoundExact.setSelection(false);
+ btnUpperBoundMicro.setSelection(false);
+ btnUpperBoundMinor.setSelection(true);
+ btnUpperBoundMajor.setSelection(false);
+ btnUpperBoundAny.setSelection(false);
+ break;
+ case Major:
+ btnUpperBoundExact.setSelection(false);
+ btnUpperBoundMicro.setSelection(false);
+ btnUpperBoundMinor.setSelection(false);
+ btnUpperBoundMajor.setSelection(true);
+ btnUpperBoundAny.setSelection(false);
+ break;
+ case Any:
+ btnUpperBoundExact.setSelection(false);
+ btnUpperBoundMicro.setSelection(false);
+ btnUpperBoundMinor.setSelection(false);
+ btnUpperBoundMajor.setSelection(false);
+ btnUpperBoundAny.setSelection(true);
+ }
+ }
+
+ private void readRadioButtons() {
+ if (btnLowerBoundExact.getSelection()) {
+ lowerBoundRule = VersionRangeBoundingRule.Exact;
+ } else if (btnLowerBoundMicro.getSelection()) {
+ lowerBoundRule = VersionRangeBoundingRule.Micro;
+ } else if (btnLowerBoundMinor.getSelection()) {
+ lowerBoundRule = VersionRangeBoundingRule.Minor;
+ } else if (btnLowerBoundMajor.getSelection()) {
+ lowerBoundRule = VersionRangeBoundingRule.Major;
+ } else if(btnLowerBoundAny.getSelection()) {
+ lowerBoundRule = VersionRangeBoundingRule.Any;
+ }
+
+ if (btnUpperBoundExact.getSelection()) {
+ upperBoundRule = VersionRangeBoundingRule.Exact;
+ } else if (btnUpperBoundMicro.getSelection()) {
+ upperBoundRule = VersionRangeBoundingRule.Micro;
+ } else if (btnUpperBoundMinor.getSelection()) {
+ upperBoundRule = VersionRangeBoundingRule.Minor;
+ } else if (btnUpperBoundMajor.getSelection()) {
+ upperBoundRule = VersionRangeBoundingRule.Major;
+ } else if(btnUpperBoundAny.getSelection()) {
+ upperBoundRule = VersionRangeBoundingRule.Any;
+ }
+ }
+
+ private void updateCalculatedRange() {
+ VersionRange range;
+ String rangeStr;
+ String matchResult;
+
+ if(sampleVersion == null) {
+ range = null;
+ rangeStr = "";
+ } else {
+ range = VersionRange.newInstance(sampleVersion, lowerBoundRule, upperBoundRule);
+ rangeStr = range.toString();
+ }
+ lblCalculatedRange.setText(rangeStr);
+
+ if(matchVersion == null || range == null) {
+ matchResult = "";
+ } else {
+ matchResult = range.contains(matchVersion) ? "MATCH!" : "No Match";
+ }
+ lblMatchResult.setText(matchResult);
+ }
+
+ public void init(IWorkbench workbench) {
+ this.workbench = workbench;
+ }
+
+ @Override
+ protected IPreferenceStore doGetPreferenceStore() {
+ return SigilCore.getDefault().getPreferenceStore();
+ }
+
+ @Override
+ public boolean performOk() {
+ getPreferenceStore().setValue(SigilCore.DEFAULT_VERSION_LOWER_BOUND, lowerBoundRule.name());
+ getPreferenceStore().setValue(SigilCore.DEFAULT_VERSION_UPPER_BOUND, upperBoundRule.name());
+
+ return true;
+ }
+
+ @Override
+ protected void performDefaults() {
+ super.performDefaults();
+ loadPreferences(true);
+ updateRadioButtons();
+ updateCalculatedRange();
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/installs/OSGiInstallsPreferencePage.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/installs/OSGiInstallsPreferencePage.java
new file mode 100644
index 0000000..9e205d3
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/installs/OSGiInstallsPreferencePage.java
@@ -0,0 +1,301 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences.installs;
+
+import java.util.HashMap;
+import java.util.UUID;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.install.IOSGiInstallType;
+import org.cauldron.sigil.ui.SigilUI;
+import org.cauldron.sigil.ui.preferences.ProjectDependentPreferencesPage;
+import org.cauldron.sigil.ui.util.DefaultTableProvider;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.viewers.CheckStateChangedEvent;
+import org.eclipse.jface.viewers.CheckboxTableViewer;
+import org.eclipse.jface.viewers.ICheckStateListener;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.swt.SWT;
+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.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.DirectoryDialog;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPreferencePage;
+
+public class OSGiInstallsPreferencePage extends ProjectDependentPreferencesPage implements
+ IWorkbenchPreferencePage {
+
+ private class Install {
+ private String id;
+ private String location;
+ private IOSGiInstallType type;
+
+ private Install(String id, String location) {
+ this.id = id;
+ this.location = location;
+ }
+
+ private IOSGiInstallType getType() {
+ if ( type == null ) {
+ type = SigilCore.getInstallManager().findInstallType(location);
+ }
+ return type;
+ }
+ }
+
+ private HashMap<String, Install> installs = new HashMap<String, Install>();
+ private CheckboxTableViewer viewer;
+ private boolean changed;
+
+ public OSGiInstallsPreferencePage() {
+ super("OSGi Installs");
+ }
+
+ public void init(IWorkbench workbench) {
+ }
+
+
+ @Override
+ protected Control createContents(Composite parent) {
+ Composite control = new Composite(parent, SWT.NONE);
+
+ buildComponents(control);
+
+ load();
+
+ checkValid();
+
+ return control;
+ }
+
+ @Override
+ protected boolean isDirty() {
+ return changed;
+ }
+
+
+ private void buildComponents(Composite control) {
+ new Label(control, SWT.NONE).setText("Installs:");
+ new Label(control, SWT.NONE); // padding
+
+ Table table = new Table(control, SWT.CHECK | SWT.SINGLE | SWT.BORDER);
+
+ Button add = new Button(control, SWT.PUSH);
+ add.setText("Add");
+ add.addSelectionListener( new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ add();
+ }
+ });
+
+ final Button remove = new Button(control, SWT.PUSH);
+ remove.setEnabled(false);
+ remove.setText("Remove");
+ remove.addSelectionListener( new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ remove();
+ }
+ });
+
+ // viewers
+ viewer = new CheckboxTableViewer(table);
+ viewer.setContentProvider( new DefaultTableProvider() {
+ public Object[] getElements(Object inputElement) {
+ return toArray(inputElement);
+ }
+ });
+
+ viewer.setLabelProvider( new LabelProvider() {
+ @Override
+ public String getText(Object element) {
+ Install i = (Install) element;
+ IOSGiInstallType type = i.getType();
+ if ( type == null ) {
+ return "<invalid> [" + i.location + "]";
+ }
+ else {
+ return type.getName() + " " + type.getVersion() + " [" + i.location + "]";
+ }
+ }
+
+ @Override
+ public Image getImage(Object element) {
+ Install i = (Install) element;
+ IOSGiInstallType type = i.getType();
+
+ if (type == null) {
+ return null;
+ } else {
+ return type.getIcon();
+ }
+ }
+ });
+
+ viewer.addSelectionChangedListener( new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent event) {
+ boolean enabled = !event.getSelection().isEmpty();
+ remove.setEnabled(enabled );
+ }
+ });
+
+ viewer.addCheckStateListener( new ICheckStateListener () {
+ public void checkStateChanged(CheckStateChangedEvent event) {
+ if ( event.getChecked() ) {
+ changed = true;
+ }
+ viewer.setCheckedElements( new Object[] { event.getElement() } );
+ }
+ });
+
+ viewer.setInput(installs.values());
+
+ // layout
+ control.setLayout( new GridLayout(2, false) );
+ table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 3));
+ add.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+ remove.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+ }
+
+ private void load() {
+ String pref = getPreferenceStore().getString(SigilCore.OSGI_INSTALLS);
+ if ( pref != null && pref.length() > 0 ) {
+ for ( String id : pref.split(",") ) {
+ String loc = getPreferenceStore().getString( SigilCore.OSGI_INSTALL_PREFIX + id );
+ installs.put( id, new Install( id, loc ) );
+ }
+ }
+
+ viewer.refresh();
+
+ if ( !installs.isEmpty() ) {
+ String defId = getPreferenceStore().getString( SigilCore.OSGI_DEFAULT_INSTALL_ID );
+ if ( defId == null || defId.trim().length() == 0 ) {
+ viewer.setCheckedElements( new Object[] { installs.values().iterator().next() } );
+ }
+ else {
+ viewer.setCheckedElements( new Object[] { installs.get( defId ) } );
+ }
+ }
+ }
+
+ protected void doSave() {
+ // zero out old configs
+ String pref = getPreferenceStore().getString(SigilCore.OSGI_INSTALLS);
+ if ( pref != null && pref.length() > 0 ) {
+ for ( String id : pref.split(",") ) {
+ getPreferenceStore().setToDefault( SigilCore.OSGI_INSTALL_PREFIX + id );
+ }
+ }
+
+ // store new configs
+ if ( installs.isEmpty() ) {
+ getPreferenceStore().setToDefault(SigilCore.OSGI_INSTALLS);
+ getPreferenceStore().setToDefault(SigilCore.OSGI_DEFAULT_INSTALL_ID);
+ }
+ else {
+ StringBuffer buf = new StringBuffer();
+ for (Install i : installs.values() ) {
+ if ( buf.length() > 0 ) {
+ buf.append(",");
+ }
+ buf.append( i.id );
+ getPreferenceStore().setValue( SigilCore.OSGI_INSTALL_PREFIX + i.id, i.location );
+ }
+
+ getPreferenceStore().setValue( SigilCore.OSGI_INSTALLS, buf.toString() );
+ Install def = (Install) viewer.getCheckedElements()[0];
+ getPreferenceStore().setValue(SigilCore.OSGI_DEFAULT_INSTALL_ID, def.id);
+ }
+ changed = false;
+ }
+
+ private boolean isOK() {
+ return installs.isEmpty() || viewer.getCheckedElements().length > 0;
+ }
+
+ private void add() {
+ Shell shell = SigilUI.getActiveWorkbenchShell();
+ DirectoryDialog dialog = new DirectoryDialog(shell);
+ String dir = dialog.open();
+ if ( dir != null ) {
+ Install install = new Install( UUID.randomUUID().toString(), dir );
+ if ( install.getType() == null ) {
+ MessageDialog.openError(shell, "Error", "Invalid OSGi install directory" );
+ }
+ else {
+ boolean empty = installs.isEmpty();
+
+ installs.put( install.id, install );
+ viewer.refresh();
+
+ if ( empty ) {
+ viewer.setCheckedElements( new Object[] { install } );
+ }
+
+ checkValid();
+ changed = true;
+ }
+ }
+ }
+
+ private void checkValid() {
+ if ( isOK() ) {
+ setErrorMessage(null);
+ setValid(true);
+ }
+ else {
+ setErrorMessage("Missing default OSGi install");
+ setValid(false);
+ }
+ }
+
+ private void remove() {
+ IStructuredSelection sel = (IStructuredSelection) viewer.getSelection();
+ Install i = (Install) sel.getFirstElement();
+ boolean def = viewer.getChecked(i);
+ installs.remove(i.id);
+ viewer.refresh();
+ if ( def && installs.size() > 0 ) {
+ viewer.setCheckedElements( new Object[] { installs.values().iterator().next() } );
+ }
+ checkValid();
+ changed = true;
+ }
+
+ @Override
+ protected IPreferenceStore doGetPreferenceStore() {
+ return SigilCore.getDefault().getPreferenceStore();
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/project/ProjectPropertyPage.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/project/ProjectPropertyPage.java
new file mode 100644
index 0000000..cbfa165
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/project/ProjectPropertyPage.java
@@ -0,0 +1,190 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences.project;
+
+import java.util.concurrent.Callable;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.util.DefaultTableProvider;
+import org.cauldron.sigil.ui.util.ProjectUtils;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.preference.PreferenceDialog;
+import org.eclipse.jface.viewers.ComboViewer;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.ui.IWorkbenchPropertyPage;
+import org.eclipse.ui.dialogs.PreferencesUtil;
+import org.eclipse.ui.dialogs.PropertyPage;
+import org.osgi.service.prefs.BackingStoreException;
+
+public class ProjectPropertyPage extends PropertyPage implements
+ IWorkbenchPropertyPage {
+
+ private boolean projectSpecific;
+ private ComboViewer setView;
+ private Composite settings;
+ private Button projectSpecificBtn;
+
+ @Override
+ protected Control createContents(Composite parent) {
+ final Composite control = new Composite(parent, SWT.NONE);
+
+ projectSpecificBtn = new Button(control, SWT.CHECK);
+ projectSpecificBtn.setText( "Enable project specific settings");
+ projectSpecificBtn.addSelectionListener( new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ setProjectSpecific(!projectSpecific);
+ }
+ });
+
+ Label link = new Label( control, SWT.UNDERLINE_SINGLE );
+ link.addMouseListener( new MouseAdapter() {
+ @Override
+ public void mouseDown(MouseEvent e) {
+ PreferenceDialog dialog = PreferencesUtil.createPreferenceDialogOn(null, SigilCore.REPOSITORIES_PREFERENCES_ID, null, null);
+ dialog.open();
+ }
+ });
+
+ link.setText("Configure workspace settings" );
+
+ settings = new Composite( control, SWT.BORDER );
+ settings.setLayout( new GridLayout(1,false));
+ createSettings( settings );
+
+ setFonts(control);
+
+ // layout
+ control.setLayout( new GridLayout(2, false));
+ projectSpecificBtn.setLayoutData( new GridData(SWT.FILL, SWT.FILL, true, false ) );
+ settings.setLayoutData( new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1 ) );
+
+ // load settings
+ String currentSet = getCurrentSet();
+
+ if ( currentSet == null ) {
+ setProjectSpecific(false);
+ }
+ else {
+ setView.setSelection( new StructuredSelection(currentSet) );
+ setProjectSpecific(true);
+ }
+
+ return control;
+ }
+
+ private void setFonts(Composite control) {
+ Composite p = control.getParent();
+ for ( Control c : control.getChildren() ) {
+ c.setFont( p.getFont() );
+ if ( c instanceof Composite ) {
+ setFonts( (Composite) c );
+ }
+ }
+ }
+
+ private void setProjectSpecific(boolean projectSpecific) {
+ if ( this.projectSpecific != projectSpecific ) {
+ this.projectSpecific = projectSpecific;
+ settings.setEnabled(projectSpecific);
+ for ( Control c : settings.getChildren() ) {
+ c.setEnabled( projectSpecific );
+ }
+ projectSpecificBtn.setSelection(projectSpecific);
+ }
+ }
+
+ private void createSettings(Composite parent) {
+ Composite control = new Composite(parent, SWT.NONE);
+
+ new Label( control, SWT.NONE).setText( "Repository Set:" );
+ Combo combo = new Combo(control, SWT.SINGLE);
+
+ setView = new ComboViewer(combo);
+ setView.setContentProvider( new DefaultTableProvider() {
+ public Object[] getElements(Object inputElement) {
+ return toArray(inputElement);
+ }
+ });
+
+ setView.setInput( SigilCore.getRepositoryConfiguration().loadRepositorySets().keySet() );
+
+ // layout
+ control.setLayout( new GridLayout(2, false ) );
+ }
+
+ private String getCurrentSet() {
+ try {
+ IProject p = (IProject) getElement().getAdapter(IProject.class);
+ ISigilProjectModel model = SigilCore.create(p);
+ return model.getPreferences().get( SigilCore.REPOSITORY_SET, null );
+ } catch (CoreException e) {
+ SigilCore.error("Failed to read repository set", e );
+ return null;
+ }
+ }
+
+ @Override
+ public boolean okToLeave() {
+ if ( projectSpecific ) {
+ if ( setView.getSelection().isEmpty() ) {
+ setErrorMessage("Must select a repository set");
+ return false;
+ }
+ }
+ setErrorMessage(null);
+ return true;
+ }
+
+ @Override
+ public boolean performOk() {
+ return ProjectUtils.runTaskWithRebuildCheck( new Callable<Boolean>() {
+ public Boolean call() throws CoreException, BackingStoreException {
+ String set = null;
+ if ( projectSpecific ) {
+ set = (String) ((IStructuredSelection) setView.getSelection()).getFirstElement();
+ }
+
+ IProject p = (IProject) getElement().getAdapter(IProject.class);
+ ISigilProjectModel model = SigilCore.create(p);
+ model.getPreferences().put( SigilCore.REPOSITORY_SET, set );
+ model.getPreferences().flush();
+ return true;
+ }
+
+ }, getShell());
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/NewRepositoryWizard.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/NewRepositoryWizard.java
new file mode 100644
index 0000000..bf410ae
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/NewRepositoryWizard.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences.repository;
+
+import org.cauldron.sigil.model.repository.IRepositoryModel;
+import org.eclipse.jface.wizard.Wizard;
+
+public class NewRepositoryWizard extends Wizard {
+
+ private IRepositoryModel repository;
+
+ private RepositoryTypeSelectionPage page = new RepositoryTypeSelectionPage();
+
+ public void addPages() {
+ addPage( page );
+ }
+
+ @Override
+ public boolean performFinish() {
+ repository = page.getRepository();
+ return true;
+ }
+ @Override
+ public boolean needsPreviousAndNextButtons() {
+ return true;
+ }
+
+ public IRepositoryModel getRepository() {
+ return repository;
+ }
+
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositoriesPreferencePage.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositoriesPreferencePage.java
new file mode 100644
index 0000000..9503a98
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositoriesPreferencePage.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences.repository;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.repository.IRepositoryConfiguration;
+import org.cauldron.sigil.model.repository.IRepositorySet;
+import org.cauldron.sigil.model.repository.RepositorySet;
+import org.cauldron.sigil.ui.preferences.ProjectDependentPreferencesPage;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.swt.SWT;
+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.TabFolder;
+import org.eclipse.swt.widgets.TabItem;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPreferencePage;
+
+public class RepositoriesPreferencePage extends ProjectDependentPreferencesPage implements
+ IWorkbenchPreferencePage {
+
+ private boolean changed;
+ private RepositoriesView viewPage;
+ private RepositorySetsView setPage;
+
+ public RepositoriesPreferencePage() {
+ super( "Repository Preferences" );
+ }
+ @Override
+ protected Control createContents(Composite parent) {
+ Control control = initContents( parent );
+ return control;
+ }
+
+ @Override
+ protected IPreferenceStore doGetPreferenceStore() {
+ return SigilCore.getDefault().getPreferenceStore();
+ }
+
+ protected void changed() {
+ changed = true;
+ updateApplyButton();
+ }
+
+ private Control initContents(Composite parent) {
+ viewPage = new RepositoriesView(this);
+ setPage = new RepositorySetsView(this);
+
+ Composite control = new Composite(parent, SWT.NONE);
+
+ TabFolder folder = new TabFolder(control, SWT.TOP);
+
+ TabItem view = new TabItem(folder, SWT.NONE);
+ view.setText("Repositories");
+ view.setControl(viewPage.createContents(folder) );
+
+ TabItem sets = new TabItem(folder, SWT.NONE);
+ sets.setText("Sets");
+ sets.setControl(setPage.createContents(folder) );
+
+ control.setLayout(new GridLayout(1, true));
+ folder.setLayoutData( new GridData(SWT.FILL, SWT.FILL, true, true));
+
+ return control;
+ }
+
+ public void init(IWorkbench workbench) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ protected void doSave() {
+ try {
+ IRepositoryConfiguration config = SigilCore.getRepositoryConfiguration();
+ config.saveRepositories(viewPage.getRepositories());
+ config.saveRepositorySets(setPage.getSets());
+ IRepositorySet defaultSet = new RepositorySet(setPage.getDefaultRepositories());
+ config.setDefaultRepositorySet(defaultSet);
+
+ setErrorMessage(null);
+ getApplyButton().setEnabled(false);
+ changed = false;
+ } catch (CoreException e) {
+ setErrorMessage("Failed to save repositories:" + e.getStatus().getMessage());
+ SigilCore.error("Failed to save repositories", e);
+ }
+ }
+
+ @Override
+ protected boolean isDirty() {
+ return changed;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositoriesView.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositoriesView.java
new file mode 100644
index 0000000..f9394e7
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositoriesView.java
@@ -0,0 +1,261 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences.repository;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.actions.RefreshRepositoryAction;
+import org.cauldron.sigil.model.repository.IRepositoryModel;
+import org.cauldron.sigil.ui.util.DefaultTableProvider;
+import org.cauldron.sigil.ui.wizard.repository.RepositoryWizard;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.window.Window;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.swt.SWT;
+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.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+
+public class RepositoriesView {
+ private final RepositoriesPreferencePage page;
+
+ private List<IRepositoryModel> repositories;
+
+ private TableViewer repositoryView;
+
+ public RepositoriesView(RepositoriesPreferencePage page) {
+ this.page = page;
+ }
+
+ public Control createContents(Composite parent) {
+ // Create Controls
+ Composite composite = new Composite(parent, SWT.NONE);
+
+ Table table = new Table(composite, SWT.MULTI | SWT.BORDER);
+
+ // Table Viewer Setup
+ repositoryView = new TableViewer(table);
+ repositoryView.setLabelProvider(new LabelProvider() {
+ @Override
+ public String getText(Object element) {
+ IRepositoryModel rep = (IRepositoryModel) element;
+ return rep.getName();
+ }
+
+ @Override
+ public Image getImage(Object element) {
+ IRepositoryModel rep = (IRepositoryModel) element;
+ return rep.getType().getIcon();
+ }
+ });
+
+ repositoryView.setContentProvider( new DefaultTableProvider() {
+ public Object[] getElements(Object inputElement) {
+ return toArray(inputElement);
+ }
+ } );
+
+ // Layout
+ composite.setLayout(new GridLayout(2, false));
+ table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 6));
+
+ createButtons(composite, repositoryView);
+
+ repositories = SigilCore.getRepositoryConfiguration().loadRepositories();
+ repositoryView.setInput(repositories);
+
+ return composite;
+ }
+
+ private void createButtons(final Composite composite, final TableViewer repositoryView) {
+ final Button add = new Button(composite, SWT.PUSH);
+ add.setText("Add...");
+ add.setEnabled(true);
+
+ final Button edit = new Button(composite, SWT.PUSH);
+ edit.setText("Edit...");
+ edit.setEnabled(false);
+
+ final Button remove = new Button(composite, SWT.PUSH);
+ remove.setText("Remove");
+ remove.setEnabled(false);
+
+ final Button refresh = new Button(composite, SWT.PUSH);
+ refresh.setText("Refresh");
+ refresh.setEnabled( false );
+
+ // Listeners
+ add.addSelectionListener( new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ add(composite);
+ }
+ });
+
+ edit.addSelectionListener( new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ IStructuredSelection sel = (IStructuredSelection) repositoryView.getSelection();
+ edit(composite, sel);
+ }
+ });
+
+ remove.addSelectionListener( new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ IStructuredSelection sel = (IStructuredSelection) repositoryView.getSelection();
+ remove(sel);
+ }
+ });
+
+ refresh.addSelectionListener( new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ IStructuredSelection sel = (IStructuredSelection) repositoryView.getSelection();
+ refresh(composite, sel);
+ }
+ });
+
+ repositoryView.addSelectionChangedListener( new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent event) {
+ boolean selected = !event.getSelection().isEmpty();
+ if ( selected ) {
+ refresh.setEnabled(true);
+
+ IStructuredSelection sel = (IStructuredSelection) event.getSelection();
+
+ checkEditEnabled(edit, sel);
+ checkRemoveEnabled(remove, sel);
+ }
+ else {
+ refresh.setEnabled(false);
+ edit.setEnabled(false);
+ remove.setEnabled(false);
+ }
+ }
+ });
+
+ add.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+ edit.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+ remove.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+ }
+
+ @SuppressWarnings("unchecked")
+ private void checkRemoveEnabled(Button button, IStructuredSelection sel) {
+ boolean alldynamic = true;
+ for ( Iterator i = sel.iterator(); i.hasNext(); ) {
+ IRepositoryModel model = (IRepositoryModel) i.next();
+ if ( !model.getType().isDynamic() ) {
+ alldynamic = false;
+ break;
+ }
+ }
+ button.setEnabled(alldynamic);
+ }
+
+ private void checkEditEnabled(Button edit, IStructuredSelection sel) {
+ if ( sel.size() == 1 ) {
+ IRepositoryModel element = (IRepositoryModel) sel.getFirstElement();
+ if ( WizardHelper.hasWizard( element.getType()) ) {
+ edit.setEnabled(true);
+ }
+ else {
+ edit.setEnabled(false);
+ }
+ }
+ else {
+ edit.setEnabled(false);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ protected void refresh(Control parent, IStructuredSelection sel) {
+ ArrayList<IRepositoryModel> models = new ArrayList<IRepositoryModel>(sel.size());
+
+ for ( Iterator i = sel.iterator(); i.hasNext(); ) {
+ IRepositoryModel model = (IRepositoryModel) i.next();
+ models.add( model );
+ }
+
+ new RefreshRepositoryAction(models.toArray(new IRepositoryModel[models.size()])).run();
+ }
+
+ private void add(Control parent) {
+ NewRepositoryWizard wizard = new NewRepositoryWizard();
+ WizardDialog dialog = new WizardDialog(getShell(parent), wizard);
+ if ( dialog.open() == Window.OK ) {
+ repositories.add(wizard.getRepository());
+ updated();
+ }
+ }
+
+ private void edit(Control parent, IStructuredSelection sel) {
+ IRepositoryModel model = (IRepositoryModel) sel.getFirstElement();
+ try {
+ RepositoryWizard wizard = WizardHelper.loadWizard(model.getType());
+ wizard.init(model);
+ WizardDialog dialog = new WizardDialog(getShell(parent), wizard);
+ if ( dialog.open() == Window.OK ) {
+ updated();
+ }
+ }
+ catch (CoreException e) {
+ SigilCore.error( "Failed to load wizard", e);
+ MessageDialog.openError(getShell(parent), "Error", "Failed to load wizard:" + e.getStatus().getMessage() );
+ }
+ }
+
+ private Shell getShell(Control parent) {
+ return parent.getShell();
+ }
+
+ @SuppressWarnings("unchecked")
+ private void remove(IStructuredSelection sel) {
+ boolean change = false;
+ for ( Iterator i = sel.iterator(); i.hasNext(); ) {
+ change = repositories.remove(i.next());
+ }
+
+ if ( change ) {
+ updated();
+ }
+ }
+
+ private void updated() {
+ repositoryView.refresh();
+ page.changed();
+ }
+
+ public List<IRepositoryModel> getRepositories() {
+ return repositories;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositorySetDialog.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositorySetDialog.java
new file mode 100644
index 0000000..8136f55
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositorySetDialog.java
@@ -0,0 +1,260 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences.repository;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.repository.IRepositoryModel;
+import org.cauldron.sigil.ui.util.DefaultLabelProvider;
+import org.cauldron.sigil.ui.util.DefaultTableProvider;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.TitleAreaDialog;
+import org.eclipse.jface.viewers.CheckboxTableViewer;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.swt.SWT;
+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.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+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;
+
+public class RepositorySetDialog extends TitleAreaDialog {
+
+ private CheckboxTableViewer viewer;
+ private Text nameTxt;
+ private Button upBtn;
+ private Button downBtn;
+ private final String setName;
+ private List<IRepositoryModel> repositories;
+ private final boolean nameEditable;
+ private final Set<String> set;
+
+ private String newName;
+
+ public RepositorySetDialog(Shell shell, Set<String> set) {
+ this(shell, null, true, set);
+ }
+
+ public RepositorySetDialog(Shell parent, RepositoryViewData data, boolean nameEditable, Set<String> set) {
+ super(parent);
+ this.set = set;
+ this.setName = data == null ? "" : data.getName();
+ this.repositories = data == null ? new ArrayList<IRepositoryModel>() : new ArrayList<IRepositoryModel>(Arrays.asList(data.getRepositories()));
+ this.nameEditable = nameEditable;
+ }
+
+ @Override
+ protected Control createDialogArea(Composite parent) {
+ Composite area = (Composite) super.createDialogArea(parent);
+ createControl(area);
+ return area;
+ }
+
+ public void createControl(Composite parent) {
+ // controls
+ Composite body = new Composite(parent, SWT.NONE);
+ body.setLayoutData( new GridData(GridData.FILL_BOTH) );
+
+ if ( nameEditable ) {
+ new Label( body, SWT.NONE ).setText( "Name" );
+
+ nameTxt = new Text( body, SWT.BORDER );
+
+ nameTxt.setText(setName);
+
+ nameTxt.addKeyListener( new KeyAdapter() {
+ @Override
+ public void keyReleased(KeyEvent e) {
+ checkComplete();
+ }
+ });
+ }
+
+ Composite table = new Composite(body, SWT.NONE);
+ table.setLayout( new GridLayout(2, false ) );
+ createTable( table );
+
+ // layout
+ body.setLayout( new GridLayout( 2, false ) );
+ if ( nameEditable ) {
+ nameTxt.setLayoutData( new GridData(SWT.FILL, SWT.FILL, true, false) );
+ }
+ table.setLayoutData( new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1) );
+ }
+
+ public RepositoryViewData getData() {
+ String name = nameEditable ? newName : setName;
+ IRepositoryModel[] reps = repositories.toArray( new IRepositoryModel[repositories.size()]);
+ return new RepositoryViewData( name, reps );
+ }
+
+ private void checkComplete() {
+ if ( nameEditable ) {
+ String name = nameTxt.getText();
+ if ( !name.equals( setName ) && set.contains( name ) ) {
+ setErrorMessage("Set " + name + " already exists" );
+ Button b = getButton(IDialogConstants.OK_ID);
+ b.setEnabled(false);
+ }
+ }
+ setErrorMessage(null);
+ Button b = getButton(IDialogConstants.OK_ID);
+ b.setEnabled(true);
+ }
+
+
+ @Override
+ protected void okPressed() {
+ if ( nameEditable ) {
+ newName = nameTxt.getText();
+ }
+ repositories = getRepositories();
+ super.okPressed();
+ }
+
+ private void createTable(Composite body) {
+ createViewer(body);
+
+ Composite btns = new Composite(body, SWT.NONE);
+ btns.setLayout( new GridLayout( 1, true ) );
+
+ createButtons(btns);
+
+ // layout
+ viewer.getTable().setLayoutData( new GridData( SWT.FILL, SWT.FILL, true, true ) );
+ btns.setLayoutData( new GridData( SWT.RIGHT, SWT.TOP, false, false ) );
+ }
+
+ private void createButtons(Composite parent) {
+ upBtn = new Button(parent, SWT.PUSH);
+ upBtn.setText( "Up" );
+ upBtn.addSelectionListener( new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ up();
+ }
+ });
+
+ downBtn = new Button(parent, SWT.PUSH);
+ downBtn.setText( "Down" );
+ downBtn.addSelectionListener( new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ down();
+ }
+ });
+
+ setUpDownEnabled(false);
+ }
+
+ private void up() {
+ IRepositoryModel model = (IRepositoryModel) ((StructuredSelection) viewer.getSelection()).getFirstElement();
+ int i = repositories.indexOf(model);
+ if ( i > 0 ) {
+ repositories.remove( i );
+ repositories.add( i - 1, model );
+ viewer.refresh();
+ }
+ }
+
+ private void down() {
+ IRepositoryModel model = (IRepositoryModel) ((StructuredSelection) viewer.getSelection()).getFirstElement();
+ int i = repositories.indexOf(model);
+ if ( i < repositories.size() - 1 ) {
+ repositories.remove( i );
+ repositories.add( i + 1, model );
+ viewer.refresh();
+ }
+ }
+
+ private void createViewer(Composite parent) {
+ viewer = CheckboxTableViewer.newCheckList(parent, SWT.BORDER);
+
+ viewer.addSelectionChangedListener( new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent event) {
+ setUpDownEnabled( !viewer.getSelection().isEmpty() );
+ }
+ });
+
+ viewer.setContentProvider( new DefaultTableProvider() {
+ public Object[] getElements(Object inputElement) {
+ return toArray(inputElement);
+ }
+ });
+
+ viewer.setLabelProvider( new DefaultLabelProvider() {
+ public Image getImage(Object element) {
+ return null;
+ }
+
+ public String getText(Object element) {
+ IRepositoryModel m = (IRepositoryModel) element;
+ return m.getName();
+ }
+ });
+
+ viewer.setInput( repositories );
+
+ for ( IRepositoryModel m : repositories ) {
+ viewer.setChecked(m, true);
+ }
+
+ List<IRepositoryModel> allRepositories = SigilCore.getRepositoryConfiguration().loadRepositories();
+
+ for ( IRepositoryModel m : allRepositories ) {
+ if ( !repositories.contains(m) ) {
+ repositories.add(m);
+ }
+ }
+
+ viewer.refresh();
+ }
+
+ private void setUpDownEnabled(boolean enabled) {
+ upBtn.setEnabled(enabled);
+ downBtn.setEnabled(enabled);
+ }
+
+ private List<IRepositoryModel> getRepositories() {
+ ArrayList<IRepositoryModel> reps = new ArrayList<IRepositoryModel>();
+
+ for ( IRepositoryModel m : repositories ) {
+ if ( viewer.getChecked(m) ) {
+ reps.add( m );
+ }
+ }
+
+ return reps;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositorySetsView.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositorySetsView.java
new file mode 100644
index 0000000..0470675
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositorySetsView.java
@@ -0,0 +1,228 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences.repository;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.repository.IRepositoryModel;
+import org.cauldron.sigil.model.repository.IRepositorySet;
+import org.cauldron.sigil.model.repository.RepositorySet;
+import org.cauldron.sigil.ui.util.DefaultLabelProvider;
+import org.cauldron.sigil.ui.util.DefaultTableProvider;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.SWT;
+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.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+
+public class RepositorySetsView {
+ private static final String DEFAULT = "default";
+
+ private final RepositoriesPreferencePage page;
+
+ private ArrayList<RepositoryViewData> sets = new ArrayList<RepositoryViewData>();
+
+ private TableViewer setView;
+
+ private RepositoryViewData defaultSet;
+
+ public RepositorySetsView(RepositoriesPreferencePage page) {
+ this.page = page;
+ }
+
+ public Control createContents(Composite parent) {
+ // Create Controls
+ Composite composite = new Composite(parent, SWT.NONE);
+
+ Table table = new Table(composite, SWT.SINGLE | SWT.BORDER);
+
+ // Table Viewer Setup
+ setView = new TableViewer(table);
+
+ setView.setContentProvider( new DefaultTableProvider() {
+ public Object[] getElements(Object inputElement) {
+ return toArray(inputElement);
+ }
+ } );
+
+ defaultSet = new RepositoryViewData(DEFAULT, SigilCore.getRepositoryConfiguration().getDefaultRepositorySet().getRepositories());
+
+ sets.add( defaultSet );
+
+ for( Map.Entry<String, IRepositorySet> e : SigilCore.getRepositoryConfiguration().loadRepositorySets().entrySet() ) {
+ IRepositorySet s = e.getValue();
+ sets.add( new RepositoryViewData( e.getKey(), s.getRepositories() ) );
+ }
+
+ setView.setLabelProvider( new DefaultLabelProvider() {
+ public Image getImage(Object element) {
+ return null;
+ }
+
+ public String getText(Object element) {
+ RepositoryViewData data = (RepositoryViewData) element;
+ return data.getName();
+ }
+ });
+
+ setView.setInput(sets);
+
+ // Layout
+ composite.setLayout(new GridLayout(2, false));
+ table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 6));
+
+ createButtons(composite);
+
+ return composite;
+ }
+
+ private void createButtons(final Composite composite) {
+ final Button add = new Button(composite, SWT.PUSH);
+ add.setText("Add...");
+ add.setEnabled(true);
+
+ final Button edit = new Button(composite, SWT.PUSH);
+ edit.setText("Edit...");
+ edit.setEnabled(false);
+
+ final Button remove = new Button(composite, SWT.PUSH);
+ remove.setText("Remove");
+ remove.setEnabled(false);
+ // Listeners
+ add.addSelectionListener( new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ add(composite);
+ }
+ });
+
+ edit.addSelectionListener( new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ IStructuredSelection sel = (IStructuredSelection) setView.getSelection();
+ edit(composite, sel);
+ }
+ });
+
+ remove.addSelectionListener( new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ IStructuredSelection sel = (IStructuredSelection) setView.getSelection();
+ remove(sel);
+ }
+ });
+
+ setView.addSelectionChangedListener( new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent event) {
+ boolean enabled = !event.getSelection().isEmpty();
+ if ( enabled ) {
+ RepositoryViewData element = (RepositoryViewData) ((IStructuredSelection) event.getSelection()).getFirstElement();
+ edit.setEnabled(true);
+ remove.setEnabled( element != defaultSet );
+ }
+ else {
+ edit.setEnabled(false);
+ remove.setEnabled(false);
+ }
+ }
+ });
+
+ add.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+ edit.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+ remove.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+ }
+
+ private void add(Control parent) {
+ RepositorySetDialog wizard = new RepositorySetDialog(getShell(parent), getNames());
+ if ( wizard.open() == Window.OK ) {
+ sets.add( wizard.getData() );
+ updated();
+ }
+ }
+
+ private void edit(Control parent, IStructuredSelection sel) {
+ RepositoryViewData data = (RepositoryViewData) sel.getFirstElement();
+ RepositorySetDialog wizard = new RepositorySetDialog(getShell(parent), data, data != defaultSet, getNames());
+ if ( wizard.open() == Window.OK ) {
+ if ( data != defaultSet ) {
+ data.setName( wizard.getData().getName() );
+ }
+ data.setRepositories( wizard.getData().getRepositories() );
+ updated();
+ }
+ }
+
+ private Set<String> getNames() {
+ HashSet<String> names = new HashSet<String>();
+
+ for ( RepositoryViewData view : sets ) {
+ if ( view != defaultSet ) {
+ names.add( view.getName() );
+ }
+ }
+
+ return names;
+ }
+
+ private Shell getShell(Control parent) {
+ return parent.getShell();
+ }
+
+ private void remove(IStructuredSelection sel) {
+ if ( sets.remove(sel.getFirstElement())) {
+ updated();
+ }
+ }
+
+ private void updated() {
+ setView.refresh();
+ page.changed();
+ }
+
+ public Map<String, IRepositorySet> getSets() {
+ HashMap<String, IRepositorySet> ret = new HashMap<String, IRepositorySet>();
+
+ for ( RepositoryViewData data : sets ) {
+ if ( data != defaultSet ) {
+ IRepositorySet set = new RepositorySet(data.getRepositories());
+ ret.put( data.getName(), set );
+ }
+ }
+
+ return ret;
+ }
+
+ public IRepositoryModel[] getDefaultRepositories() {
+ return defaultSet.getRepositories();
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositoryTypeSelectionPage.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositoryTypeSelectionPage.java
new file mode 100644
index 0000000..3dfe8b8
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositoryTypeSelectionPage.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences.repository;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.repository.IRepositoryModel;
+import org.cauldron.sigil.model.repository.IRepositoryType;
+import org.cauldron.sigil.ui.util.DefaultTableProvider;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.wizard.IWizardPage;
+import org.eclipse.jface.wizard.WizardSelectionPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Table;
+
+public class RepositoryTypeSelectionPage extends WizardSelectionPage implements IWizardPage {
+
+ private static final String TITLE = "Select Repository";
+
+ private TableViewer repositoryView;
+ private IRepositoryModel repositoryElement;
+
+ public RepositoryTypeSelectionPage() {
+ super(TITLE);
+ setTitle(TITLE);
+ }
+
+ public void createControl(Composite parent) {
+ Composite control = new Composite(parent, SWT.NONE);
+
+ // components
+ new Label(control, SWT.NONE).setText("Repositories" );
+ Table table = new Table(control, SWT.SINGLE | SWT.BORDER);
+
+ // layout
+ control.setLayout( new GridLayout(1, true) );
+ table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+ // view
+ repositoryView = new TableViewer(table);
+ repositoryView.setLabelProvider( new LabelProvider() {
+ @Override
+ public String getText(Object element) {
+ IRepositoryType rep = (IRepositoryType) element;
+ return rep.getType();
+ }
+
+ @Override
+ public Image getImage(Object element) {
+ IRepositoryType rep = (IRepositoryType) element;
+ return rep.getIcon();
+ }
+ });
+
+ repositoryView.setContentProvider( new DefaultTableProvider() {
+ public Object[] getElements(Object inputElement) {
+ return toArray(inputElement);
+ }
+ } );
+
+ repositoryView.addSelectionChangedListener( new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent event) {
+ if ( !event.getSelection().isEmpty() ) {
+ IStructuredSelection sel = (IStructuredSelection) event.getSelection();
+ IRepositoryType type = (IRepositoryType) sel.getFirstElement();
+ repositoryElement = SigilCore.getRepositoryConfiguration().newRepositoryElement(type);
+ selectWizardNode(new RepositoryWizardNode(repositoryElement));
+ }
+ }
+ });
+
+ ArrayList<IRepositoryType> descriptors = new ArrayList<IRepositoryType>(SigilCore.getRepositoryConfiguration().loadRepositoryTypes());
+
+ for ( Iterator<IRepositoryType> i = descriptors.iterator(); i.hasNext(); ) {
+ if ( !i.next().isDynamic() ) {
+ i.remove();
+ }
+ }
+
+ repositoryView.setInput( descriptors );
+
+ setControl(control);
+ }
+
+ public void selectWizardNode(RepositoryWizardNode node) {
+ setSelectedNode(node);
+ }
+
+ public RepositoryWizardNode getSelectedWizardNode() {
+ return (RepositoryWizardNode) getSelectedNode();
+ }
+
+ public IRepositoryModel getRepository() {
+ return repositoryElement;
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositoryViewData.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositoryViewData.java
new file mode 100644
index 0000000..85124c7
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositoryViewData.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences.repository;
+
+import org.cauldron.sigil.model.repository.IRepositoryModel;
+
+class RepositoryViewData {
+ private String name;
+ private IRepositoryModel[] reps;
+
+ RepositoryViewData(String name, IRepositoryModel[] reps) {
+ this.name = name;
+ this.reps = reps;
+ }
+
+ String getName() {
+ return name;
+ }
+
+ IRepositoryModel[] getRepositories() {
+ return reps;
+ }
+
+ void setName(String name) {
+ this.name = name;
+ }
+
+ void setRepositories(IRepositoryModel[] reps) {
+ this.reps = reps;
+ }
+
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositoryWizardNode.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositoryWizardNode.java
new file mode 100644
index 0000000..5ea458a
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/RepositoryWizardNode.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences.repository;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.repository.IRepositoryModel;
+import org.cauldron.sigil.ui.wizard.repository.RepositoryWizard;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.wizard.IWizard;
+import org.eclipse.jface.wizard.IWizardNode;
+import org.eclipse.swt.graphics.Point;
+
+public class RepositoryWizardNode implements IWizardNode {
+
+ private IRepositoryModel repository;
+
+ private RepositoryWizard wizard;
+
+ public RepositoryWizardNode(IRepositoryModel repository) {
+ this.repository = repository;
+ }
+
+ public void dispose() {
+ if ( wizard != null ) {
+ wizard.dispose();
+ wizard = null;
+ }
+ }
+
+ public Point getExtent() {
+ return new Point(-1, -1);
+ }
+
+ public IWizard getWizard() {
+ if ( wizard == null ) {
+ try {
+ wizard = WizardHelper.loadWizard(repository.getType());
+ wizard.init( repository );
+ } catch (CoreException e) {
+ SigilCore.error( "Failed to create wizard for " + repository.getType(), e);
+ }
+ }
+ return wizard;
+ }
+
+ public IRepositoryModel getRepository() {
+ return repository;
+ }
+
+ public boolean isContentCreated() {
+ return wizard != null;
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/WizardHelper.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/WizardHelper.java
new file mode 100644
index 0000000..101b57e
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/preferences/repository/WizardHelper.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.preferences.repository;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.repository.IRepositoryType;
+import org.cauldron.sigil.ui.SigilUI;
+import org.cauldron.sigil.ui.wizard.repository.RepositoryWizard;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtension;
+import org.eclipse.core.runtime.IExtensionPoint;
+import org.eclipse.core.runtime.IExtensionRegistry;
+import org.eclipse.core.runtime.Platform;
+
+public class WizardHelper {
+ public static RepositoryWizard loadWizard(IRepositoryType type) throws CoreException {
+ IConfigurationElement e = findWizardConfig(type.getId());
+
+ if ( e == null ) {
+ throw SigilCore.newCoreException("No wizard registered for repository " + type, null);
+ }
+
+ return (RepositoryWizard) e.createExecutableExtension("class");
+ }
+
+ public static boolean hasWizard(IRepositoryType type) {
+ return findWizardConfig(type.getId()) != null;
+ }
+
+ private static IConfigurationElement findWizardConfig(String id) {
+ IExtensionRegistry registry = Platform.getExtensionRegistry();
+ IExtensionPoint p = registry.getExtensionPoint(SigilUI.REPOSITORY_WIZARD_EXTENSION_POINT_ID);
+
+ for ( IExtension e : p.getExtensions() ) {
+ for ( IConfigurationElement c : e.getConfigurationElements() ) {
+ if ( id.equals( c.getAttribute("repository") ) ) {
+ return c;
+ }
+ }
+ }
+
+ return null;
+ }
+
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/quickfix/ImportPackageProposal.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/quickfix/ImportPackageProposal.java
new file mode 100644
index 0000000..163154d
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/quickfix/ImportPackageProposal.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.quickfix;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.ModelElementFactoryException;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.common.VersionRangeBoundingRule;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.ui.SigilUI;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jdt.ui.text.java.IJavaCompletionProposal;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.contentassist.IContextInformation;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.ui.actions.WorkspaceModifyOperation;
+import org.osgi.framework.Version;
+
+public class ImportPackageProposal implements IJavaCompletionProposal {
+
+ private IPackageExport e;
+ private ISigilProjectModel n;
+
+ public ImportPackageProposal(IPackageExport e, ISigilProjectModel n) {
+ this.e = e;
+ this.n = n;
+ }
+
+ public int getRelevance() {
+ return 100;
+ }
+
+ public void apply(IDocument document) {
+ try {
+
+ final IPackageImport i = ModelElementFactory.getInstance().newModelElement(IPackageImport.class);
+ i.setPackageName(e.getPackageName());
+ IPreferenceStore store = SigilCore.getDefault().getPreferenceStore();
+ VersionRangeBoundingRule lowerBoundRule = VersionRangeBoundingRule.valueOf(store.getString(SigilCore.DEFAULT_VERSION_LOWER_BOUND));
+ VersionRangeBoundingRule upperBoundRule = VersionRangeBoundingRule.valueOf(store.getString(SigilCore.DEFAULT_VERSION_UPPER_BOUND));
+
+ Version version = e.getVersion();
+ VersionRange selectedVersions = VersionRange.newInstance(version, lowerBoundRule, upperBoundRule);
+ i.setVersions( selectedVersions );
+
+ WorkspaceModifyOperation op = new WorkspaceModifyOperation() {
+ @Override
+ protected void execute(IProgressMonitor monitor)
+ throws CoreException {
+ n.getBundle().getBundleInfo().addImport( i );
+ n.save(null);
+ }
+ };
+
+ SigilUI.runWorkspaceOperation(op, null);
+ } catch (ModelElementFactoryException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ public String getAdditionalProposalInfo() {
+ return null;
+ }
+
+ public IContextInformation getContextInformation() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public String getDisplayString() {
+ return "Import package " + e.getPackageName() + " version " + e.getVersion() + " to bundle";
+ }
+
+ public Image getImage() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public Point getSelection(IDocument document) {
+ return null;
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/quickfix/ImportQuickFixProcessor.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/quickfix/ImportQuickFixProcessor.java
new file mode 100644
index 0000000..ac3f9ba
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/quickfix/ImportQuickFixProcessor.java
@@ -0,0 +1,253 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.quickfix;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.model.util.JavaHelper;
+import org.cauldron.sigil.search.ISearchResult;
+import org.cauldron.sigil.search.SigilSearch;
+import org.cauldron.sigil.ui.SigilUI;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.compiler.IProblem;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.ArrayType;
+import org.eclipse.jdt.core.dom.ClassInstanceCreation;
+import org.eclipse.jdt.core.dom.ImportDeclaration;
+import org.eclipse.jdt.core.dom.MethodInvocation;
+import org.eclipse.jdt.core.dom.Name;
+import org.eclipse.jdt.core.dom.QualifiedName;
+import org.eclipse.jdt.core.dom.SimpleType;
+import org.eclipse.jdt.core.dom.Type;
+import org.eclipse.jdt.internal.corext.dom.ASTNodes;
+import org.eclipse.jdt.ui.text.java.IInvocationContext;
+import org.eclipse.jdt.ui.text.java.IJavaCompletionProposal;
+import org.eclipse.jdt.ui.text.java.IProblemLocation;
+import org.eclipse.jdt.ui.text.java.IQuickFixProcessor;
+
+@SuppressWarnings("restriction")
+public class ImportQuickFixProcessor implements IQuickFixProcessor {
+
+ private static final Object SYNC_FLAG = new Object();
+
+ public boolean hasCorrections(ICompilationUnit unit, int problemId) {
+ switch ( problemId ) {
+ case IProblem.ImportNotFound:
+ case IProblem.ForbiddenReference:
+ case IProblem.NotVisibleType:
+ case IProblem.UndefinedType:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public IJavaCompletionProposal[] getCorrections(IInvocationContext context,
+ IProblemLocation[] locations) throws CoreException {
+ try {
+ HashMap<Object, IJavaCompletionProposal> results = new HashMap<Object, IJavaCompletionProposal>();
+
+ ISigilProjectModel project = findProject(context);
+
+ if ( project != null ) {
+ for ( int i = 0; i < locations.length; i++ ) {
+ switch ( locations[i].getProblemId() ) {
+ case IProblem.ForbiddenReference:
+ handleImportNotFound(project, context, locations[i], results);
+ break;
+ case IProblem.ImportNotFound:
+ handleImportNotFound(project, context, locations[i], results);
+ break;
+ case IProblem.IsClassPathCorrect:
+ handleIsClassPathCorrect(project, context, locations[i], results);
+ break;
+ case IProblem.UndefinedType:
+ handleUndefinedType(project, context, locations[i], results);
+ break;
+ case IProblem.UndefinedName:
+ handleUndefinedName(project, context, locations[i], results);
+ break;
+ }
+ }
+ }
+
+ return (IJavaCompletionProposal[])results.values().toArray(new IJavaCompletionProposal[results.size()]);
+ }
+ catch ( RuntimeException e ) {
+ e.printStackTrace();
+ throw e;
+ }
+ }
+
+ private void handleUndefinedName(ISigilProjectModel project,
+ IInvocationContext context, IProblemLocation problem,
+ HashMap<Object, IJavaCompletionProposal> results) {
+ Name node = findNode(context, problem);
+
+ if ( node == null ) {
+ return;
+ }
+ addSearchResults(node, project, results);
+ }
+
+ private void handleIsClassPathCorrect(ISigilProjectModel project, final IInvocationContext context,
+ IProblemLocation problemLocation,
+ final HashMap<Object, IJavaCompletionProposal> results) {
+ for ( final String type : problemLocation.getProblemArguments() ) {
+ final String iPackage = type.substring(0, type.lastIndexOf("."));
+
+ for ( IPackageExport pe : JavaHelper.findExportsForPackage(project, iPackage) ) {
+ results.put( type, new ImportPackageProposal( pe, project ) );
+ }
+ }
+
+ if ( !results.containsKey(SYNC_FLAG) ) {
+ //results.put( SYNC_FLAG, null);
+ }
+ }
+
+ private void handleUndefinedType(ISigilProjectModel project, IInvocationContext context,
+ IProblemLocation problem,
+ HashMap<Object, IJavaCompletionProposal> results) throws CoreException {
+ Name node = findNode(context, problem);
+
+ if ( node == null ) {
+ return;
+ }
+ addSearchResults(node, project, results);
+ }
+
+ private void handleImportNotFound(ISigilProjectModel project, final IInvocationContext context, IProblemLocation location, final HashMap<Object, IJavaCompletionProposal> results) throws CoreException {
+ ASTNode selectedNode= location.getCoveringNode(context.getASTRoot());
+ if (selectedNode == null) return;
+
+ if ( selectedNode instanceof ClassInstanceCreation ) {
+ ClassInstanceCreation c = (ClassInstanceCreation) selectedNode;
+ Type t = c.getType();
+ Name node = findName( t );
+ if ( node != null ) {
+ addSearchResults(node, project, results);
+ }
+ }
+ else {
+ for ( final String iPackage : readPackage(selectedNode, location) ) {
+ if ( !results.containsKey(iPackage) ) {
+ for ( IPackageExport pe : JavaHelper.findExportsForPackage(project, iPackage) ) {
+ results.put( iPackage, new ImportPackageProposal( pe, project ) );
+ }
+ }
+ }
+ }
+ }
+
+ private void addSearchResults(Name node, ISigilProjectModel project,
+ HashMap<Object, IJavaCompletionProposal> results) {
+ for ( ISearchResult result : SigilSearch.findProviders( node.getFullyQualifiedName(), project, null) ) {
+ if ( project.getBundle().findImport( result.getPackageName() ) == null ) {
+ String type = result.getPackageName() + "." + node.getFullyQualifiedName();
+ results.put( type, new ImportSearchResultProposal( SigilUI.getActiveWorkbenchShell(), result, node, project ) );
+ }
+ }
+ }
+
+ private Name findName(Type t) {
+ if ( t.isSimpleType() ) {
+ SimpleType st = (SimpleType) t;
+ return st.getName();
+ }
+ else if ( t.isArrayType() ) {
+ ArrayType at = (ArrayType) t;
+ return findName(at.getElementType());
+ }
+ else {
+ return null;
+ }
+ }
+
+ private Name findNode(IInvocationContext context, IProblemLocation problem) {
+ ASTNode selectedNode= problem.getCoveringNode(context.getASTRoot());
+ if (selectedNode == null) {
+ return null;
+ }
+
+ while (selectedNode.getLocationInParent() == QualifiedName.NAME_PROPERTY) {
+ selectedNode= selectedNode.getParent();
+ }
+
+ Name node= null;
+
+ if (selectedNode instanceof Type) {
+ node = findName( (Type) selectedNode );
+ } else if (selectedNode instanceof Name) {
+ node= (Name) selectedNode;
+ }
+
+ return node;
+ }
+
+ private ISigilProjectModel findProject(IInvocationContext context) throws CoreException {
+ IProject project = context.getCompilationUnit().getJavaProject().getProject();
+ if ( project.hasNature( SigilCore.NATURE_ID ) ) {
+ return SigilCore.create(project);
+ }
+ else {
+ return null;
+ }
+ }
+
+ private String[] readPackage(ASTNode selectedNode, IProblemLocation location) {
+ ArrayList<String> packages = new ArrayList<String>();
+
+ ImportDeclaration id = (ImportDeclaration) ASTNodes.getParent(selectedNode, ASTNode.IMPORT_DECLARATION);
+
+ if ( id == null ) {
+ MethodInvocation m = (MethodInvocation) ASTNodes.getParent(selectedNode, ASTNode.METHOD_INVOCATION);
+
+ if (m != null) {
+ packages.add( readPackage( m ) );
+ while ( m.getExpression() != null && m.getExpression() instanceof MethodInvocation) {
+ m = (MethodInvocation) m.getExpression();
+ packages.add( readPackage( m ) );
+ }
+ }
+ }
+ else {
+ if ( id.isOnDemand() ) {
+ packages.add(id.getName().toString());
+ }
+ else {
+ String iStr = id.getName().toString();
+ packages.add(iStr.substring(0, iStr.lastIndexOf( "." ) ));
+ }
+ }
+
+ return packages.toArray( new String[packages.size()] );
+ }
+
+ private String readPackage(MethodInvocation m) {
+ return m.resolveMethodBinding().getDeclaringClass().getPackage().getName();
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/quickfix/ImportSearchResultProposal.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/quickfix/ImportSearchResultProposal.java
new file mode 100644
index 0000000..fbbf524
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/quickfix/ImportSearchResultProposal.java
@@ -0,0 +1,156 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.quickfix;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.ModelElementFactory;
+import org.cauldron.sigil.model.ModelElementFactoryException;
+import org.cauldron.sigil.model.common.VersionRange;
+import org.cauldron.sigil.model.common.VersionRangeBoundingRule;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.search.ISearchResult;
+import org.cauldron.sigil.ui.SigilUI;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.Name;
+import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
+import org.eclipse.jdt.internal.corext.dom.ASTNodes;
+import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
+import org.eclipse.jdt.ui.CodeStyleConfiguration;
+import org.eclipse.jdt.ui.text.java.IJavaCompletionProposal;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.contentassist.IContextInformation;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.actions.WorkspaceModifyOperation;
+import org.osgi.framework.Version;
+
+public class ImportSearchResultProposal implements IJavaCompletionProposal {
+
+ private ISigilProjectModel project;
+ private ICompilationUnit fCompilationUnit;
+ private final ISearchResult result;
+ private final Shell shell;
+
+ public ImportSearchResultProposal(Shell shell, ISearchResult result, Name node, ISigilProjectModel project) {
+ this.shell = shell;
+ this.result = result;
+ this.project = project;
+ if ( node != null ) {
+ CompilationUnit cu = (CompilationUnit) ASTNodes.getParent(node, ASTNode.COMPILATION_UNIT);
+ this.fCompilationUnit = (ICompilationUnit) cu.getJavaElement();
+ }
+ }
+
+ public int getRelevance() {
+ return 100;
+ }
+
+ public void apply(IDocument document) {
+ IPackageExport e = result.getExport();
+ if ( result.getExport() == null ) {
+ if ( MessageDialog.openQuestion(shell, "Modify " + result.getProvider().getBundleInfo().getSymbolicName(), result.getPackageName() + " is not exported. Do you want to export it now?" ) ) {
+ final IPackageExport pe = ModelElementFactory.getInstance().newModelElement(IPackageExport.class);
+ pe.setPackageName(result.getPackageName());
+ //e.setVersion(version)
+ final ISigilProjectModel mod = result.getProvider().getAncestor(ISigilProjectModel.class);
+ if ( mod == null ) {
+ throw new IllegalStateException( "Attempt to modify binary package export" );
+ }
+ WorkspaceModifyOperation op = new WorkspaceModifyOperation() {
+ @Override
+ protected void execute(IProgressMonitor monitor)
+ throws CoreException {
+ mod.getBundle().getBundleInfo().addExport(pe);
+ mod.save(null);
+ }
+ };
+
+ SigilUI.runWorkspaceOperation(op, null);
+ e = pe;
+ }
+ }
+
+ final IPackageImport i = ModelElementFactory.getInstance().newModelElement(IPackageImport.class);
+ i.setPackageName(e.getPackageName());
+ IPreferenceStore store = SigilCore.getDefault().getPreferenceStore();
+ VersionRangeBoundingRule lowerBoundRule = VersionRangeBoundingRule.valueOf(store.getString(SigilCore.DEFAULT_VERSION_LOWER_BOUND));
+ VersionRangeBoundingRule upperBoundRule = VersionRangeBoundingRule.valueOf(store.getString(SigilCore.DEFAULT_VERSION_UPPER_BOUND));
+
+ Version version = e.getVersion();
+ VersionRange selectedVersions = VersionRange.newInstance(version, lowerBoundRule, upperBoundRule);
+ i.setVersions( selectedVersions );
+
+ WorkspaceModifyOperation op = new WorkspaceModifyOperation() {
+ @Override
+ protected void execute(IProgressMonitor monitor)
+ throws CoreException {
+ project.getBundle().getBundleInfo().addImport( i );
+ project.save(null);
+ }
+ };
+
+ SigilUI.runWorkspaceOperation(op, null);
+ addSourceImport();
+ }
+
+ private void addSourceImport() {
+ // add import
+ try {
+ ImportRewrite rewrite= CodeStyleConfiguration.createImportRewrite(fCompilationUnit, true);
+ rewrite.addImport(result.getClassName());
+ JavaModelUtil.applyEdit(fCompilationUnit, rewrite.rewriteImports(null), false, null);
+ } catch (CoreException e) {
+ SigilCore.error( "Failed to add import", e);
+ }
+ }
+
+ public String getAdditionalProposalInfo() {
+ return null;
+ }
+
+ public IContextInformation getContextInformation() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public String getDisplayString() {
+ String type = result.getClassName();
+ String loc = result.getExport() == null ? " from " + result.getProvider().getBundleInfo().getSymbolicName() : " version " + result.getExport().getVersion();
+ return "Import " + type + loc;
+ }
+
+ public Image getImage() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public Point getSelection(IDocument document) {
+ return null;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/quickfix/ImportedClassReference.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/quickfix/ImportedClassReference.java
new file mode 100644
index 0000000..3a264c2
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/quickfix/ImportedClassReference.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.quickfix;
+
+import org.cauldron.sigil.model.osgi.IPackageImport;
+
+public class ImportedClassReference {
+ private IPackageImport pi;
+ private String type;
+
+ public ImportedClassReference(IPackageImport packageImport, String typeName) {
+ this.pi = packageImport;
+ this.type = typeName;
+ }
+
+ public IPackageImport getPackageImport() {
+ return pi;
+ }
+
+ public String getFullType() {
+ return type;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/refactor/RenameCompositeRefactoring.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/refactor/RenameCompositeRefactoring.java
new file mode 100644
index 0000000..ce5075f
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/refactor/RenameCompositeRefactoring.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.refactor;
+
+import java.util.Map;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.ltk.core.refactoring.Change;
+import org.eclipse.ltk.core.refactoring.Refactoring;
+import org.eclipse.ltk.core.refactoring.RefactoringContribution;
+import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
+import org.eclipse.ltk.core.refactoring.RefactoringStatus;
+
+public class RenameCompositeRefactoring extends Refactoring {
+
+ @Override
+ public RefactoringStatus checkFinalConditions(IProgressMonitor pm) throws CoreException,
+ OperationCanceledException {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException,
+ OperationCanceledException {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public Change createChange(IProgressMonitor pm) throws CoreException,
+ OperationCanceledException {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public String getName() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/AccumulatorAdapter.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/AccumulatorAdapter.java
new file mode 100644
index 0000000..3ad4fe9
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/AccumulatorAdapter.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+public abstract class AccumulatorAdapter<E> implements IAccumulator<E> {
+ public void addElement(E element) {
+ LinkedList<E> list = new LinkedList<E>();
+ list.add(element);
+ addElements(list);
+ };
+
+ public void addElements(Collection<? extends E> elements) {
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/BackgroundLoadingSelectionDialog.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/BackgroundLoadingSelectionDialog.java
new file mode 100644
index 0000000..8ad02de
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/BackgroundLoadingSelectionDialog.java
@@ -0,0 +1,487 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.cauldron.sigil.ui.editors.project.IElementDescriptor;
+import org.cauldron.sigil.ui.editors.project.WrappedContentProposal;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.TitleAreaDialog;
+import org.eclipse.jface.fieldassist.ContentProposalAdapter;
+import org.eclipse.jface.fieldassist.ControlDecoration;
+import org.eclipse.jface.fieldassist.FieldDecoration;
+import org.eclipse.jface.fieldassist.FieldDecorationRegistry;
+import org.eclipse.jface.fieldassist.IContentProposal;
+import org.eclipse.jface.fieldassist.IContentProposalListener;
+import org.eclipse.jface.fieldassist.TextContentAdapter;
+import org.eclipse.jface.viewers.ArrayContentProvider;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.IOpenListener;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.viewers.OpenEvent;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerFilter;
+import org.eclipse.jface.viewers.ViewerSorter;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTError;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.progress.IJobRunnable;
+
+public class BackgroundLoadingSelectionDialog<E> extends TitleAreaDialog implements IAccumulator<E> {
+
+ private final ILabelProvider DEFAULT_LABEL_PROVIDER = new LabelProvider() {
+ @SuppressWarnings("unchecked")
+ public String getText(Object element) {
+ String result;
+ if(element instanceof WrappedContentProposal<?>) {
+ WrappedContentProposal<E> contentProposal = (WrappedContentProposal<E>) element;
+ result = contentProposal.getLabel();
+ } else {
+ result = descriptor.getLabel((E) element);
+ }
+ return result;
+ }
+ };
+
+ private final IElementDescriptor<E> DEFAULT_DESCRIPTOR = new IElementDescriptor<E>() {
+ public String getLabel(E element) {
+ return getName(element);
+ }
+ public String getName(E element) {
+ return element == null ? "null" : element.toString();
+ }
+ };
+
+ private final String selectionLabel;
+ private IFilter<? super E> filter;
+ private IElementDescriptor<? super E> descriptor = DEFAULT_DESCRIPTOR;
+ private ILabelProvider labelProvider = DEFAULT_LABEL_PROVIDER;
+ private final boolean multi;
+
+ private final List<E> elements;
+
+ private List<E> selection = null;
+ private String selectedName = null;
+
+ private TableViewer viewer = null;
+ private Comparator<? super E> comparator;
+
+ private HashMap<String, IJobRunnable> background = new HashMap<String, IJobRunnable>();
+
+ public BackgroundLoadingSelectionDialog(Shell parentShell, String selectionLabel, boolean multi) {
+ super(parentShell);
+ elements = new ArrayList<E>();
+ this.selectionLabel = selectionLabel;
+ this.multi = multi;
+ }
+
+ public void setFilter(IFilter<? super E> filter) {
+ this.filter = filter;
+ }
+
+ public void setDescriptor(final IElementDescriptor<? super E> descriptor) {
+ if(descriptor != null) {
+ this.descriptor = descriptor;
+ } else {
+ this.descriptor = DEFAULT_DESCRIPTOR;
+ }
+ }
+
+ public IElementDescriptor<? super E> getDescriptor() {
+ return descriptor;
+ }
+
+ public void setComparator(Comparator<? super E> comparator) {
+ this.comparator = comparator;
+ }
+
+ public void setLabelProvider(ILabelProvider labelProvider) {
+ if(labelProvider != null) {
+ this.labelProvider = labelProvider;
+ } else {
+ this.labelProvider = DEFAULT_LABEL_PROVIDER;
+ }
+ }
+
+ public void addBackgroundJob(String name, IJobRunnable job) {
+ background.put(name, job);
+ }
+
+ @Override
+ public int open() {
+ Job[] jobs = scheduleJobs();
+ try {
+ return super.open();
+ }
+ finally {
+ for ( Job j : jobs ) {
+ j.cancel();
+ }
+ }
+ }
+
+ private Job[] scheduleJobs() {
+ if ( background.isEmpty() ) {
+ return new Job[] {};
+ }
+ else {
+ ArrayList<Job> jobs = new ArrayList<Job>(background.size());
+ for ( Map.Entry<String, IJobRunnable> e : background.entrySet() ) {
+ final IJobRunnable run = e.getValue();
+ Job job = new Job(e.getKey()) {
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ return run.run(monitor);
+ }
+ };
+ job.schedule();
+ }
+
+ return jobs.toArray( new Job[jobs.size()] );
+ }
+ }
+
+ @Override
+ protected Control createDialogArea(Composite parent) {
+ // Create Controls
+ Composite container = (Composite) super.createDialogArea(parent);
+ Composite composite = new Composite(container, SWT.NONE);
+
+ new Label(composite, SWT.NONE).setText(selectionLabel);
+
+ ContentProposalAdapter proposalAdapter = null;
+ Text txtSelection = null;
+
+ Table table = null;
+ if(multi) {
+ table = new Table(composite, SWT.MULTI | SWT.FULL_SELECTION | SWT.BORDER);
+ viewer = new TableViewer(table);
+ viewer.setContentProvider(new ArrayContentProvider());
+ viewer.addFilter(new ViewerFilter() {
+ public boolean select(Viewer viewer, Object parentElement, Object element) {
+ @SuppressWarnings("unchecked") E castedElement = (E) element;
+ return filter == null || filter.select(castedElement);
+ }
+ });
+ if(comparator != null) {
+ viewer.setSorter(new ViewerSorter() {
+ @Override
+ public int compare(Viewer viewer, Object o1, Object o2) {
+ @SuppressWarnings("unchecked")
+ E e1 = (E) o1;
+ @SuppressWarnings("unchecked")
+ E e2 = (E) o2;
+ return comparator.compare(e1, e2);
+ }
+ });
+ }
+ synchronized (elements) {
+ viewer.setInput(elements);
+ }
+
+ if(labelProvider != null) {
+ viewer.setLabelProvider(labelProvider);
+ }
+ } else {
+ txtSelection = new Text(composite, SWT.BORDER);
+ ControlDecoration selectionDecor = new ControlDecoration(txtSelection, SWT.LEFT | SWT.TOP);
+ FieldDecoration proposalDecor = FieldDecorationRegistry.getDefault().getFieldDecoration(FieldDecorationRegistry.DEC_CONTENT_PROPOSAL);
+ selectionDecor.setImage(proposalDecor.getImage());
+ selectionDecor.setDescriptionText(proposalDecor.getDescription());
+
+ ExclusionContentProposalProvider<E> proposalProvider = new ExclusionContentProposalProvider<E>(elements, filter, descriptor);
+
+ proposalAdapter = new ContentProposalAdapter(txtSelection, new TextContentAdapter(), proposalProvider, null, null);
+ proposalAdapter.setProposalAcceptanceStyle(ContentProposalAdapter.PROPOSAL_REPLACE);
+ if(labelProvider != null) {
+ proposalAdapter.setLabelProvider(labelProvider);
+ }
+
+ if(selectedName != null) {
+ txtSelection.setText(selectedName);
+ }
+ }
+ updateSelection();
+ updateButtons();
+
+ // Hookup listeners
+ if(proposalAdapter != null) {
+ proposalAdapter.addContentProposalListener(new IContentProposalListener() {
+ public void proposalAccepted(IContentProposal proposal) {
+ @SuppressWarnings("unchecked")
+ WrappedContentProposal<E> valueProposal = (WrappedContentProposal<E>) proposal;
+ E selected = valueProposal.getElement();
+ selection = new ArrayList<E>(1);
+ selection.add(selected);
+
+ elementSelected(selected);
+
+ updateButtons();
+ }
+ });
+ }
+ if(txtSelection != null) {
+ txtSelection.addModifyListener(new ModifyListener() {
+ public void modifyText(ModifyEvent e) {
+ selectedName = ((Text) e.widget).getText();
+ updateButtons();
+ }
+ });
+ }
+ if(viewer != null) {
+ viewer.addSelectionChangedListener(new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent event) {
+ IStructuredSelection sel = (IStructuredSelection) event.getSelection();
+ selection = new ArrayList<E>(sel.size());
+ for(Iterator<?> iter = sel.iterator(); iter.hasNext(); ) {
+ @SuppressWarnings("unchecked")
+ E element = (E) iter.next();
+ selection.add(element);
+ }
+ updateButtons();
+ }
+ });
+ viewer.addOpenListener(new IOpenListener() {
+ public void open(OpenEvent event) {
+ if(canComplete()) {
+ setReturnCode(IDialogConstants.OK_ID);
+ close();
+ }
+ }
+ });
+ }
+
+ // Layout
+ composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ if(multi) {
+ composite.setLayout(new GridLayout(1, false));
+ GridData layoutTable = new GridData(SWT.FILL, SWT.FILL, true, true);
+ layoutTable.heightHint = 200;
+ table.setLayoutData(layoutTable);
+ } else {
+ composite.setLayout(new GridLayout(2, false));
+ txtSelection.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ }
+
+ return container;
+ }
+
+ protected void elementSelected(E selection) {
+ }
+
+ @Override
+ protected Control createButtonBar(Composite parent) {
+ Control bar = super.createButtonBar(parent);
+ updateButtons();
+ return bar;
+ }
+
+ /**
+ * Can be called from any thread
+ */
+ protected final void updateButtons() {
+ Runnable updateButtonsRunnable = new Runnable() {
+ public void run() {
+ Shell shell = getShell();
+ if(shell != null && !shell.isDisposed()) {
+ Button okButton = getButton(IDialogConstants.OK_ID);
+ if(okButton != null && !okButton.isDisposed()) {
+ okButton.setEnabled(canComplete());
+ }
+ }
+ }
+ };
+ Shell shell = getShell();
+ if (shell != null) {
+ onUIThread(shell, updateButtonsRunnable);
+ }
+ }
+
+ /**
+ * Subclasses may override but must call super.canComplete
+ * @return
+ */
+ protected synchronized boolean canComplete() {
+ boolean result = false;
+
+ if ( selection != null ) {
+ if(multi) {
+ result = selection.size() > 0;
+ } else {
+ E sel = getSelectedElement();
+ result = sel != null && descriptor.getName(sel).equals(selectedName);
+ }
+ }
+
+ return result;
+ }
+
+ public final void addElement(E added) {
+ addElements(Collections.singleton(added));
+ }
+
+ /**
+ * Can be called from any thread
+ */
+ public final void addElements(Collection<? extends E> added) {
+ final LinkedList<E> toAdd = new LinkedList<E>();
+ synchronized (elements) {
+ for ( E e : added ) {
+ if ( !elements.contains(e) ) {
+ elements.add( e );
+ toAdd.add(e);
+ }
+ }
+ Collections.sort(elements, comparator);
+ }
+ if(viewer != null) {
+ onUIThread(viewer.getControl(), new Runnable() {
+ public void run() {
+ if(!viewer.getControl().isDisposed()) {
+ viewer.add( toAdd.toArray() );
+ viewer.refresh();
+ }
+ }
+ });
+ }
+ else {
+
+ }
+ updateSelection();
+ updateButtons();
+ }
+
+ protected void updateSelection() {
+ onUIThread(getShell(), new Runnable() {
+ public void run() {
+ if(selectedName != null) {
+ ArrayList<E> newSelection = new ArrayList<E>();
+ synchronized (elements) {
+ for (E e : elements) {
+ if(selectedName.equals(descriptor.getName(e))) {
+ newSelection.add(e);
+ break;
+ }
+ }
+ }
+ selection = newSelection;
+ }
+ else {
+ selection = Collections.emptyList();
+ }
+ if(viewer != null && !viewer.getControl().isDisposed()) {
+ viewer.setSelection(selection.isEmpty() ? StructuredSelection.EMPTY : new StructuredSelection(selection));
+ }
+ }
+ });
+ }
+
+ private static final void onUIThread(Control control, Runnable r) {
+ if(control != null && !control.isDisposed()) {
+ try {
+ Display display = control.getDisplay();
+ if(Thread.currentThread() == display.getThread()) {
+ // We are on the UI thread already, just do the work
+ r.run();
+ } else {
+ // Not on the UI thread, need to bung over the runnable
+ display.asyncExec(r);
+ }
+ }
+ catch (SWTError e) {
+ if ( e.code == SWT.ERROR_WIDGET_DISPOSED ) {
+ // ignore
+ }
+ else {
+ throw e;
+ }
+ }
+ }
+ }
+
+ public String getSelectedName() {
+ return selectedName;
+ }
+
+ public void setSelectedName(String selectedName) {
+ this.selectedName = selectedName;
+ boolean change = false;
+ if ( selectedName == null ) {
+ if ( selection != null && !selection.isEmpty() ) {
+ change = true;
+ }
+ }
+ else {
+ if ( selection == null ) {
+ change = true;
+ }
+ else if ( selection.size() != 1 || !descriptor.getLabel(selection.get(0)).equals(selectedName)) {
+ change = true;
+ }
+ }
+
+ if ( change ) {
+ updateSelection();
+ updateButtons();
+ }
+ }
+
+ public List<E> getSelectedElements() {
+ return selection;
+ }
+
+ public E getSelectedElement() {
+ E result;
+ if(selection == null || selection.isEmpty()) {
+ result = null;
+ } else {
+ result = selection.get(0);
+ }
+ return result;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ColumnModelLabelProvider.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ColumnModelLabelProvider.java
new file mode 100644
index 0000000..2febfbd
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ColumnModelLabelProvider.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.swt.graphics.Image;
+
+public class ColumnModelLabelProvider extends ColumnLabelProvider {
+
+ private ModelLabelProvider provider = new ModelLabelProvider();
+
+ @Override
+ public Image getImage(Object element) {
+ return provider.getImage(element);
+ }
+
+ @Override
+ public String getText(Object element) {
+ return provider.getText(element);
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/DefaultContentProvider.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/DefaultContentProvider.java
new file mode 100644
index 0000000..5fb4237
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/DefaultContentProvider.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import org.eclipse.jface.viewers.IContentProvider;
+import org.eclipse.jface.viewers.Viewer;
+
+public class DefaultContentProvider implements IContentProvider {
+
+ public void dispose() {
+ }
+
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/DefaultLabelProvider.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/DefaultLabelProvider.java
new file mode 100644
index 0000000..29fb24b
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/DefaultLabelProvider.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import org.eclipse.jface.viewers.IBaseLabelProvider;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.ILabelProviderListener;
+
+public abstract class DefaultLabelProvider implements IBaseLabelProvider, ILabelProvider {
+
+ public boolean isLabelProperty(Object element, String property) {
+ return false;
+ }
+
+ public void dispose() {
+ }
+
+ public void addListener(ILabelProviderListener listener) {
+ }
+
+ public void removeListener(ILabelProviderListener listener) {
+ }
+
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/DefaultTableProvider.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/DefaultTableProvider.java
new file mode 100644
index 0000000..715afa9
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/DefaultTableProvider.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import java.util.Collection;
+
+import org.eclipse.jface.viewers.IStructuredContentProvider;
+
+public abstract class DefaultTableProvider extends DefaultContentProvider implements IStructuredContentProvider {
+
+ /**
+ * Utility method to convert the input element to an Object[].
+ *
+ * @param inputElement
+ *
+ * @return if inputElement is null -> empty array <br/>
+ * if inputElement is a {@link Collection} returns {@link Collection#toArray()}<br/>
+ * if inputElement is an Array class cast of inputElement to Object[]<br/>
+ *
+ * @throws IllegalArgumentException if the element cannot be converted.
+ */
+ public Object[] toArray(Object inputElement) {
+ if ( inputElement == null ) {
+ return new Object[] {};
+ }
+ else if ( inputElement instanceof Collection ) {
+ Collection<?> col = (Collection<?>) inputElement;
+ return col.toArray();
+ }
+ else if ( inputElement.getClass().isArray() ) {
+ return (Object[]) inputElement;
+ }
+ else {
+ throw new IllegalArgumentException( "Invalid inputElement " + inputElement.getClass() );
+ }
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/DefaultTreeContentProvider.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/DefaultTreeContentProvider.java
new file mode 100644
index 0000000..d223835
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/DefaultTreeContentProvider.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import org.eclipse.jface.viewers.ITreeContentProvider;
+
+public abstract class DefaultTreeContentProvider extends DefaultContentProvider implements ITreeContentProvider {
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ExclusionContentProposalProvider.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ExclusionContentProposalProvider.java
new file mode 100644
index 0000000..1663e2f
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ExclusionContentProposalProvider.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.cauldron.sigil.ui.editors.project.IElementDescriptor;
+import org.cauldron.sigil.ui.editors.project.WrappedContentProposal;
+import org.cauldron.sigil.utils.GlobCompiler;
+import org.eclipse.jface.fieldassist.IContentProposal;
+import org.eclipse.jface.fieldassist.IContentProposalProvider;
+
+public class ExclusionContentProposalProvider<T> implements
+ IContentProposalProvider {
+
+ private final Collection<? extends T> elements;
+ private final IFilter<? super T> filter;
+ private final IElementDescriptor<? super T> descriptor;
+
+ public ExclusionContentProposalProvider(Collection<? extends T> elements, IFilter<? super T> filter, IElementDescriptor<? super T> descriptor) {
+ this.elements = elements;
+ this.filter = filter;
+ this.descriptor = descriptor;
+ }
+
+ public IContentProposal[] getProposals(String contents, int position) {
+ String matchString = contents.substring(0, position);
+ Pattern pattern = GlobCompiler.compile(matchString);
+
+ // Get a snapshot of the elements
+ T[] elementArray;
+ synchronized (elements) {
+ @SuppressWarnings("unchecked") T[] temp = (T[]) elements.toArray();
+ elementArray = temp;
+ }
+
+ List<IContentProposal> result = new ArrayList<IContentProposal>();
+
+ for (T element : elementArray) {
+ if(filter != null && filter.select(element)) {
+ IContentProposal proposal = WrappedContentProposal.newInstance(element, descriptor);
+ Matcher matcher = pattern.matcher(proposal.getContent());
+ if(matcher.find()) {
+ result.add(proposal);
+ }
+ }
+ }
+
+ return result.toArray(new IContentProposal[result.size()]);
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ExportedPackageFinder.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ExportedPackageFinder.java
new file mode 100644
index 0000000..5c85724
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ExportedPackageFinder.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.IModelWalker;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.ui.progress.IJobRunnable;
+
+public class ExportedPackageFinder implements IJobRunnable {
+
+ private final IAccumulator<? super IPackageExport> accumulator;
+ private final ISigilProjectModel sigil;
+
+ public ExportedPackageFinder(ISigilProjectModel sigil, IAccumulator<? super IPackageExport> accumulator) {
+ this.sigil = sigil;
+ this.accumulator = accumulator;
+ }
+
+ public IStatus run(final IProgressMonitor monitor) {
+ final List<IPackageExport> exports = new ArrayList<IPackageExport>(ResourcesDialogHelper.UPDATE_BATCH_SIZE);
+ final IModelWalker walker = new IModelWalker() {
+ public boolean visit(IModelElement element) {
+ if ( element instanceof IPackageExport ) {
+ IPackageExport pkgExport = (IPackageExport) element;
+ exports.add(pkgExport);
+
+ if(exports.size() >= ResourcesDialogHelper.UPDATE_BATCH_SIZE) {
+ accumulator.addElements(exports);
+ exports.clear();
+ }
+ }
+ return !monitor.isCanceled();
+ }
+ };
+ SigilCore.getRepositoryManager(sigil).visit(walker);
+ if(exports.size() > 0) {
+ accumulator.addElements(exports);
+ }
+
+ return Status.OK_STATUS;
+ }
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/FileUtils.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/FileUtils.java
new file mode 100644
index 0000000..60cb121
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/FileUtils.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.DirectoryDialog;
+import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
+
+public class FileUtils {
+ public static void loadFile( Shell shell, Text text, String msg, boolean isDirectory ) {
+ if ( isDirectory ) {
+ DirectoryDialog dialog = new DirectoryDialog(shell, SWT.NONE);
+ dialog.setMessage(msg);
+ String value = dialog.open();
+ if ( value != null ) {
+ text.setText( value );
+ }
+ }
+ else {
+ FileDialog dialog = new FileDialog(shell, SWT.NONE);
+ dialog.setText(msg);
+ String value = dialog.open();
+ if ( value != null ) {
+ text.setText( value );
+ }
+ }
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/IAccumulator.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/IAccumulator.java
new file mode 100644
index 0000000..99384be
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/IAccumulator.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import java.util.Collection;
+
+public interface IAccumulator<E> {
+ public void addElement(E element);
+ public void addElements(Collection<? extends E> elements);
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/IExportToImportConverter.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/IExportToImportConverter.java
new file mode 100644
index 0000000..915d16c
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/IExportToImportConverter.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+public interface IExportToImportConverter<E,I> {
+ I convert(E exportElement);
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/IFilter.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/IFilter.java
new file mode 100644
index 0000000..6cd3f23
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/IFilter.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+public interface IFilter<T> {
+ boolean select(T element);
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/IValidationListener.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/IValidationListener.java
new file mode 100644
index 0000000..6c1b701
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/IValidationListener.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+public interface IValidationListener {
+
+ void validationMessage(String message, int level);
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ModelLabelProvider.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ModelLabelProvider.java
new file mode 100644
index 0000000..6ff6901
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ModelLabelProvider.java
@@ -0,0 +1,185 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import java.util.Set;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.cauldron.sigil.model.repository.IRepositoryModel;
+import org.cauldron.sigil.repository.IBundleRepository;
+import org.cauldron.sigil.ui.SigilUI;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.IPackageFragment;
+import org.eclipse.jdt.core.IPackageFragmentRoot;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.swt.graphics.Image;
+import org.osgi.framework.Version;
+
+public class ModelLabelProvider extends LabelProvider {
+
+ private volatile Set<? extends IModelElement> unresolvedElements = null;
+
+ public Image getImage(Object element) {
+ boolean unresolved = (unresolvedElements == null) ? false : unresolvedElements.contains(element);
+
+ if ( element instanceof ISigilBundle || element instanceof IBundleModelElement) {
+ return findBundle();
+ } else if(element instanceof IRequiredBundle) {
+ boolean optional = ((IRequiredBundle) element).isOptional();
+ return findRequiredBundle(optional, unresolved);
+ }
+ else if ( element instanceof IPackageImport ) {
+ boolean optional = ((IPackageImport) element).isOptional();
+ return findPackageImport(optional, unresolved);
+ }
+ else if ( element instanceof IPackageExport ) {
+ return findPackageExport();
+ }
+ else if ( element instanceof IPackageFragmentRoot ) {
+ IPackageFragmentRoot root = (IPackageFragmentRoot) element;
+ try {
+ if ( root.getKind() == IPackageFragmentRoot.K_SOURCE ) {
+ return findPackage();
+ }
+ else {
+ return findBundle();
+ }
+ } catch (JavaModelException e) {
+ SigilCore.error( "Failed to inspect package fragment root", e );
+ }
+ }
+ else if ( element instanceof IClasspathEntry ) {
+ return findPackage();
+ }
+ if ( element instanceof IBundleRepository ) {
+ IBundleRepository rep = (IBundleRepository) element;
+ IRepositoryModel config = SigilCore.getRepositoryConfiguration().findRepository(rep.getId());
+ return config.getType().getIcon();
+ }
+
+ return null;
+ }
+
+ public String getText(Object element) {
+ if ( element instanceof ISigilBundle ) {
+ ISigilBundle bundle = (ISigilBundle) element;
+ return bundle.getBundleInfo().getSymbolicName() + " " + bundle.getBundleInfo().getVersion();
+ }
+ if ( element instanceof IBundleModelElement ) {
+ IBundleModelElement bundle = (IBundleModelElement) element;
+ return bundle.getSymbolicName();
+ }
+ if ( element instanceof IRequiredBundle ) {
+ IRequiredBundle req = (IRequiredBundle) element;
+ return req.getSymbolicName() + " " + req.getVersions();
+ }
+
+ if ( element instanceof IPackageImport ) {
+ IPackageImport req = (IPackageImport) element;
+ return req.getPackageName() + " " + req.getVersions();
+ }
+
+ if ( element instanceof IPackageExport ) {
+ IPackageExport pe = (IPackageExport) element;
+ Version rawVersion = pe.getRawVersion();
+ return rawVersion != null ? pe.getPackageName() + " " + rawVersion : pe.getPackageName();
+ }
+
+ if ( element instanceof IResource ) {
+ IResource resource = (IResource) element;
+ return resource.getName();
+ }
+
+ if ( element instanceof IPackageFragment ) {
+ IPackageFragment f = (IPackageFragment) element;
+ return f.getElementName();
+ }
+
+ if ( element instanceof IPackageFragmentRoot ) {
+ IPackageFragmentRoot f = (IPackageFragmentRoot) element;
+ try {
+ return f.getUnderlyingResource().getName();
+ } catch (JavaModelException e) {
+ return "unknown";
+ }
+ }
+
+ if ( element instanceof IClasspathEntry ) {
+ IClasspathEntry cp = (IClasspathEntry) element;
+ return cp.getPath().toString();
+ }
+
+ if ( element instanceof IBundleRepository ) {
+ IBundleRepository rep = (IBundleRepository) element;
+ IRepositoryModel config = SigilCore.getRepositoryConfiguration().findRepository(rep.getId());
+ return config.getName();
+ }
+
+ return element.toString();
+ }
+
+ private Image findPackage() {
+ return cacheImage( "icons/package_obj.png" );
+ }
+
+ private Image findPackageImport(boolean optional, boolean unresolved) {
+ String path;
+ if(optional) {
+ path = unresolved ? "icons/package_obj_import_opt_error.png" : "icons/package_obj_import_opt.png";
+ } else {
+ path = unresolved ? "icons/package_obj_import_error.png" : "icons/package_obj_import.png";
+ }
+ return cacheImage(path);
+ }
+
+ private Image findPackageExport() {
+ return cacheImage("icons/package_obj_export.png");
+ }
+
+ private Image findBundle() {
+ return cacheImage("icons/jar_obj.png");
+ }
+
+ private Image findRequiredBundle(boolean optional, boolean unresolved) {
+ String path;
+ if(optional) {
+ path = unresolved ? "icons/required_bundle_opt_error.png" : "icons/required_bundle_opt.png";
+ } else {
+ path = unresolved ? "icons/required_bundle_error.png" : "icons/jar_obj.png";
+ }
+ return cacheImage(path);
+ }
+
+ private static Image cacheImage(String path) {
+ return SigilUI.cacheImage(path, ModelLabelProvider.class.getClassLoader());
+ }
+
+ public void setUnresolvedElements(Set<? extends IModelElement> elements) {
+ this.unresolvedElements = elements;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/PackageFilter.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/PackageFilter.java
new file mode 100644
index 0000000..d451538
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/PackageFilter.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+
+public class PackageFilter implements IFilter<IPackageImport> {
+
+ private Set<String> names = new HashSet<String>();
+
+ public PackageFilter(String[] packageNames) {
+ for (String name : packageNames) {
+ names.add(name);
+ }
+ }
+
+ public PackageFilter(IPackageExport[] packages) {
+ for (IPackageExport packageExport : packages) {
+ names.add(packageExport.getPackageName());
+ }
+ }
+
+ public boolean select(IPackageImport element) {
+ return !names.contains(element.getPackageName());
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ProjectUtils.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ProjectUtils.java
new file mode 100644
index 0000000..4cf8ad4
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ProjectUtils.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import java.util.concurrent.Callable;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.ui.SigilUI;
+import org.cauldron.sigil.ui.preferences.OptionalPrompt;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.actions.WorkspaceModifyOperation;
+
+public class ProjectUtils {
+ public static boolean runTaskWithRebuildCheck(final Runnable task, Shell shell) {
+ return runTaskWithRebuildCheck( new Callable<Boolean>() {
+ public Boolean call() throws Exception {
+ task.run();
+ return true;
+ }
+ }, shell);
+ }
+
+ public static boolean runTaskWithRebuildCheck(Callable<Boolean> callable,
+ Shell shell) {
+ int result = checkRebuild(shell);
+ if ( result == IDialogConstants.CANCEL_ID ) {
+ return false;
+ }
+ else {
+ try {
+ if ( Boolean.TRUE == callable.call() ) {
+ if ( result == IDialogConstants.YES_ID ) {
+ SigilUI.runWorkspaceOperation( new WorkspaceModifyOperation() {
+ @Override
+ protected void execute(IProgressMonitor monitor) {
+ SigilCore.rebuildAllBundleDependencies(monitor);
+ }
+ }, shell );
+ }
+ return true;
+ }
+ else {
+ return false;
+ }
+ } catch (Exception e) {
+ SigilCore.error( "Failed to run caller", e);
+ return false;
+ }
+ }
+ }
+
+ private static int checkRebuild(Shell shell) {
+ if ( SigilCore.getRoot().getProjects().isEmpty() ) {
+ return IDialogConstants.NO_ID;
+ }
+ else {
+ return OptionalPrompt.optionallyPromptWithCancel(SigilCore.getDefault().getPreferenceStore(), SigilCore.PREFERENCES_REBUILD_PROJECTS, "Rebuild", "Do you wish to rebuild all Sigil projects", shell );
+ }
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ResourceReviewDialog.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ResourceReviewDialog.java
new file mode 100644
index 0000000..bebe867
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ResourceReviewDialog.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import java.util.Collection;
+
+import org.cauldron.sigil.model.IModelElement;
+import org.eclipse.jface.dialogs.TitleAreaDialog;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+
+public class ResourceReviewDialog<T extends IModelElement> extends TitleAreaDialog {
+
+ private String title;
+ private Collection<T> resources;
+
+ private TableViewer viewer;
+
+ public ResourceReviewDialog(Shell parentShell, String title, Collection<T> resources) {
+ super(parentShell);
+ this.title = title;
+ this.resources = resources;
+ }
+
+ public Collection<T> getResources() {
+ return resources;
+ }
+
+ @Override
+ protected Control createDialogArea(Composite parent) {
+ setTitle(title);
+
+ // Create controls
+ Composite container = (Composite) super.createDialogArea(parent);
+ Composite composite = new Composite(container, SWT.NONE);
+ Table table = new Table(composite, SWT.BORDER | SWT.VIRTUAL);
+
+ final Button remove = new Button(composite, SWT.PUSH);
+ remove.setText("Remove");
+ remove.setEnabled(false);
+
+ remove.addSelectionListener( new SelectionAdapter() {
+ @Override
+ public void widgetSelected(SelectionEvent e) {
+ handleRemove();
+ }
+ } );
+
+ viewer = new TableViewer(table);
+ viewer.setContentProvider( new DefaultTableProvider() {
+ public Object[] getElements(Object inputElement) {
+ return toArray(inputElement);
+ }
+ });
+
+ viewer.setInput( resources );
+ viewer.addSelectionChangedListener( new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent event) {
+ remove.setEnabled(!event.getSelection().isEmpty());
+ }
+ });
+
+ // layout
+ composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ composite.setLayout(new GridLayout(2, false));
+ GridData tableLayoutData = new GridData(SWT.FILL, SWT.FILL, true, true, 1, 4);
+ tableLayoutData.heightHint = 150;
+ table.setLayoutData(tableLayoutData);
+
+ return container;
+ }
+
+ private void handleRemove() {
+ ISelection s = viewer.getSelection();
+ if ( !s.isEmpty() ) {
+ IStructuredSelection sel = (IStructuredSelection) s;
+ resources.remove( sel.getFirstElement() );
+ viewer.refresh();
+ }
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ResourcesDialogHelper.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ResourcesDialogHelper.java
new file mode 100644
index 0000000..4644fe3
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/ResourcesDialogHelper.java
@@ -0,0 +1,290 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.IModelWalker;
+import org.cauldron.sigil.model.osgi.IBundleModelElement;
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IPackageModelElement;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.model.util.JavaHelper;
+import org.cauldron.sigil.ui.editors.project.IElementDescriptor;
+import org.cauldron.sigil.ui.editors.project.NewPackageExportDialog;
+import org.cauldron.sigil.ui.editors.project.NewResourceSelectionDialog;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IPackageFragment;
+import org.eclipse.jdt.core.IPackageFragmentRoot;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.ITypeRoot;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.progress.IJobRunnable;
+
+public class ResourcesDialogHelper {
+
+ static final int UPDATE_BATCH_SIZE = 100;
+
+ public static BackgroundLoadingSelectionDialog<String> createClassSelectDialog(Shell shell, String title, final ISigilProjectModel project, String selected, final String ifaceOrParentClass) {
+ final BackgroundLoadingSelectionDialog<String> dialog = new BackgroundLoadingSelectionDialog<String>(shell, "Class Name", true);
+
+ IJobRunnable job = new IJobRunnable() {
+ public IStatus run(IProgressMonitor monitor) {
+ try {
+ for ( IJavaElement e : JavaHelper.findTypes(project.getJavaModel(), IJavaElement.PACKAGE_FRAGMENT_ROOT ) ) {
+ IPackageFragmentRoot root = (IPackageFragmentRoot) e;
+ if ( project.isInBundleClasspath(root)) {
+ for ( IJavaElement e1 : JavaHelper.findTypes(root, IJavaElement.COMPILATION_UNIT, IJavaElement.CLASS_FILE ) ) {
+ ITypeRoot typeRoot = (ITypeRoot) e1;
+ IType type = (IType) JavaHelper.findType(typeRoot, IJavaElement.TYPE);
+ if ( JavaHelper.isAssignableTo( ifaceOrParentClass, type ) ) {
+ dialog.addElement(type.getFullyQualifiedName());
+ }
+ }
+ }
+ }
+
+ return Status.OK_STATUS;
+ } catch (JavaModelException e) {
+ return e.getStatus();
+ }
+ }
+
+ };
+
+ dialog.addBackgroundJob("Scanning for activators in project", job);
+
+ return dialog;
+ }
+
+ public static NewResourceSelectionDialog<IPackageExport> createImportDialog(
+ Shell shell,
+ String title,
+ ISigilProjectModel sigil,
+ final IPackageImport selected,
+ final Collection<IPackageImport> existing) {
+ final Set<String> existingNames = new HashSet<String>();
+
+ for (IPackageImport existingImport : existing) {
+ existingNames.add(existingImport.getPackageName());
+ }
+
+ final NewResourceSelectionDialog<IPackageExport> dialog = new NewResourceSelectionDialog<IPackageExport>(shell, "Package Name:", false);
+
+ dialog.setFilter( new IFilter<IPackageModelElement>() {
+ public boolean select(IPackageModelElement element) {
+ return !existingNames.contains(element.getPackageName());
+ }
+ } );
+
+ dialog.setComparator(new Comparator<IPackageExport>() {
+ public int compare(IPackageExport o1, IPackageExport o2) {
+ return o1.compareTo(o2);
+ }
+ });
+
+ dialog.setDescriptor(new IElementDescriptor<IPackageExport>() {
+ public String getLabel(IPackageExport element) {
+ return getName(element) + " (" + element.getVersion().toString() + ")";
+ }
+
+ public String getName(IPackageExport element) {
+ return element.getPackageName();
+ }
+ });
+
+ dialog.setLabelProvider(new WrappedContentProposalLabelProvider<IPackageExport>(dialog.getDescriptor()));
+
+ if(selected != null) {
+ dialog.setSelectedName(selected.getPackageName());
+ dialog.setVersions(selected.getVersions());
+ dialog.setOptional(selected.isOptional());
+ }
+
+ IJobRunnable job = new ExportedPackageFinder(sigil, dialog);
+ dialog.addBackgroundJob("Scanning for exports in workspace", job);
+
+ return dialog;
+ }
+
+ public static NewPackageExportDialog createNewExportDialog(Shell shell, String title, final IPackageExport selected, final ISigilProjectModel project, boolean multiSelect) {
+ IFilter<IJavaElement> selectFilter = new IFilter<IJavaElement>() {
+ public boolean select(IJavaElement e) {
+ if ( selected != null && e.getElementName().equals( selected.getPackageName() ) ) {
+ return true;
+ }
+
+ if ( e.getElementName().trim().length() > 0 && isLocal( e ) ) {
+ for ( IPackageExport p : project.getBundle().getBundleInfo().getExports() ) {
+ if ( p.getPackageName().equals( e.getElementName() ) ) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+
+ private boolean isLocal(IJavaElement java) {
+ try {
+ switch ( java.getElementType() ) {
+ case IJavaElement.PACKAGE_FRAGMENT:
+ IPackageFragment fragment= (IPackageFragment) java;
+ return fragment.containsJavaResources();
+ default:
+ throw new IllegalStateException( "Unexpected resource type " + java );
+ }
+ }
+ catch (JavaModelException e) {
+ SigilCore.error( "Failed to inspect java element ", e );
+ return false;
+ }
+ }
+
+ };
+
+ final NewPackageExportDialog dialog = new NewPackageExportDialog(shell, multiSelect);
+ dialog.setFilter(selectFilter);
+
+ dialog.setProjectVersion(project.getVersion());
+ if ( selected != null ) {
+ dialog.setSelectedName(selected.getPackageName());
+ dialog.setVersion(selected.getRawVersion());
+ }
+
+ IJobRunnable job = new IJobRunnable() {
+ public IStatus run(IProgressMonitor monitor) {
+ try {
+ ArrayList<IPackageFragment> list = new ArrayList<IPackageFragment>(UPDATE_BATCH_SIZE);
+ for ( IJavaElement e : JavaHelper.findTypes(project.getJavaModel(), IJavaElement.PACKAGE_FRAGMENT_ROOT) ) {
+ IPackageFragmentRoot root = (IPackageFragmentRoot) e;
+ if ( project.isInBundleClasspath(root) ) {
+ for ( IJavaElement e1 : JavaHelper.findTypes(root, IJavaElement.PACKAGE_FRAGMENT) ) {
+ list.add((IPackageFragment) e1);
+ if(list.size() >= UPDATE_BATCH_SIZE) {
+ dialog.addElements(list);
+ list.clear();
+ }
+ }
+ }
+ }
+ if(!list.isEmpty()) {
+ dialog.addElements(list);
+ }
+ return Status.OK_STATUS;
+ } catch (JavaModelException e) {
+ return e.getStatus();
+ }
+ }
+ };
+
+ dialog.addBackgroundJob("Scanning for packages in project", job);
+
+ return dialog;
+ }
+
+ public static NewResourceSelectionDialog<IBundleModelElement> createRequiredBundleDialog(Shell shell, String title, final ISigilProjectModel sigil, final IRequiredBundle selected, final Collection<IRequiredBundle> existing) {
+ final Set<String> existingNames = new HashSet<String>();
+ for(IRequiredBundle existingBundle : existing) {
+ existingNames.add(existingBundle.getSymbolicName());
+ }
+
+ final NewResourceSelectionDialog<IBundleModelElement> dialog = new NewResourceSelectionDialog<IBundleModelElement>(shell, "Bundle:", false);
+
+ dialog.setDescriptor(new IElementDescriptor<IBundleModelElement>() {
+ public String getLabel(IBundleModelElement element) {
+ return getName(element) + " (" + element.getVersion() + ")";
+ }
+ public String getName(IBundleModelElement element) {
+ return element.getSymbolicName();
+ }
+ });
+
+ dialog.setLabelProvider(new WrappedContentProposalLabelProvider<IBundleModelElement>(dialog.getDescriptor()));
+
+ dialog.setFilter(new IFilter<IBundleModelElement>() {
+ public boolean select(IBundleModelElement element) {
+ return !existingNames.contains(element.getSymbolicName());
+ }
+ });
+
+ dialog.setComparator(new Comparator<IBundleModelElement>() {
+ public int compare(IBundleModelElement o1, IBundleModelElement o2) {
+ return o1.getSymbolicName().compareTo(o2.getSymbolicName());
+ }
+ });
+
+ if(selected != null) {
+ dialog.setSelectedName(selected.getSymbolicName());
+ dialog.setVersions(selected.getVersions());
+ dialog.setOptional(selected.isOptional());
+ }
+
+ IJobRunnable job = new IJobRunnable() {
+ public IStatus run(final IProgressMonitor monitor) {
+ final List<IBundleModelElement> bundles = new ArrayList<IBundleModelElement>(UPDATE_BATCH_SIZE);
+ final IModelWalker walker = new IModelWalker() {
+ //int count = 0;
+ public boolean visit(IModelElement element) {
+ if ( element instanceof IBundleModelElement) {
+ IBundleModelElement b = (IBundleModelElement) element;
+ bundles.add(b);
+
+ if(bundles.size() >= UPDATE_BATCH_SIZE) {
+ dialog.addElements(bundles);
+ bundles.clear();
+ }
+ // no need to recurse further.
+ return false;
+ }
+ return !monitor.isCanceled();
+ }
+ };
+ SigilCore.getRepositoryManager(sigil).visit(walker);
+ if(!bundles.isEmpty()) {
+ dialog.addElements(bundles);
+ }
+ return Status.OK_STATUS;
+ }
+ };
+
+ dialog.addBackgroundJob("Scanning for bundles in workspace", job);
+
+ return dialog;
+ }
+}
+
+
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/SingletonSelection.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/SingletonSelection.java
new file mode 100644
index 0000000..22a0a46
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/SingletonSelection.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.jface.viewers.IStructuredSelection;
+
+@SuppressWarnings("unchecked")
+public class SingletonSelection implements IStructuredSelection {
+
+ private final Object singleton;
+
+ public SingletonSelection(Object singleton) {
+ this.singleton = singleton;
+ }
+
+ public Object getFirstElement() {
+ return singleton;
+ }
+
+ public Iterator iterator() {
+ return Collections.singleton(singleton).iterator();
+ }
+
+ public int size() {
+ return 1;
+ }
+
+ public Object[] toArray() {
+ return new Object[] { singleton };
+ }
+
+ public List toList() {
+ ArrayList list = new ArrayList(1);
+ list.add( singleton );
+ return list;
+ }
+
+ public boolean isEmpty() {
+ return false;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/WrappedContentProposalLabelProvider.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/WrappedContentProposalLabelProvider.java
new file mode 100644
index 0000000..f46b4b2
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/util/WrappedContentProposalLabelProvider.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.util;
+
+import org.cauldron.sigil.ui.editors.project.IElementDescriptor;
+import org.cauldron.sigil.ui.editors.project.WrappedContentProposal;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.swt.graphics.Image;
+
+public class WrappedContentProposalLabelProvider<E> extends LabelProvider {
+
+ private final IElementDescriptor<? super E> descriptor;
+ private final ModelLabelProvider projectLabelProvider;
+
+ public WrappedContentProposalLabelProvider(IElementDescriptor<? super E> descriptor) {
+ this.descriptor = descriptor;
+ projectLabelProvider = new ModelLabelProvider();
+ }
+
+ @SuppressWarnings("unchecked")
+ private E adapt(Object element) {
+ E result;
+ if(element instanceof WrappedContentProposal<?>) {
+ WrappedContentProposal<?> proposal = (WrappedContentProposal<?>) element;
+ result = (E) proposal.getElement();
+ } else {
+ result = (E) element;
+ }
+ return result;
+ }
+
+ @Override
+ public Image getImage(Object element) {
+ Object value = adapt(element);
+ return projectLabelProvider.getImage(value);
+ }
+
+ @Override
+ public String getText(Object element) {
+ return descriptor.getLabel(adapt(element));
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/ModelElementComparator.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/ModelElementComparator.java
new file mode 100644
index 0000000..cbfbbbe
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/ModelElementComparator.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.views;
+
+import org.cauldron.sigil.model.osgi.IPackageExport;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.eclipse.jface.viewers.ContentViewer;
+import org.eclipse.jface.viewers.IBaseLabelProvider;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerComparator;
+
+public class ModelElementComparator extends ViewerComparator {
+ private static final int EXPORT_GROUP = 0;
+ private static final int IMPORT_GROUP = 1;
+ private static final int REQUIRE_GROUP = 2;
+ private static final int OTHER_GROUP = 4;
+ public int category(Object element) {
+ if ( element instanceof IPackageImport ) {
+ return IMPORT_GROUP;
+ }
+ else if ( element instanceof IPackageExport ) {
+ return EXPORT_GROUP;
+ }
+ else if ( element instanceof IRequiredBundle ) {
+ return REQUIRE_GROUP;
+ }
+ else {
+ return OTHER_GROUP;
+ }
+ }
+ @SuppressWarnings("unchecked")
+ @Override
+ public int compare(Viewer viewer, Object e1, Object e2) {
+ int cat1 = category(e1);
+ int cat2 = category(e2);
+
+ if (cat1 != cat2) {
+ return cat1 - cat2;
+ }
+
+ if ( cat1 == OTHER_GROUP ) {
+ String name1;
+ String name2;
+
+ if (viewer == null || !(viewer instanceof ContentViewer)) {
+ name1 = e1.toString();
+ name2 = e2.toString();
+ } else {
+ IBaseLabelProvider prov = ((ContentViewer) viewer)
+ .getLabelProvider();
+ if (prov instanceof ILabelProvider) {
+ ILabelProvider lprov = (ILabelProvider) prov;
+ name1 = lprov.getText(e1);
+ name2 = lprov.getText(e2);
+ } else {
+ name1 = e1.toString();
+ name2 = e2.toString();
+ }
+ }
+ if (name1 == null) {
+ name1 = "";//$NON-NLS-1$
+ }
+ if (name2 == null) {
+ name2 = "";//$NON-NLS-1$
+ }
+
+ // use the comparator to compare the strings
+ return getComparator().compare(name1, name2);
+ }
+ else {
+ Comparable c1 = (Comparable) e1;
+ Comparable c2 = (Comparable) e2;
+ return c1.compareTo(c2);
+ }
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/RepositoryViewPart.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/RepositoryViewPart.java
new file mode 100644
index 0000000..4fdb238
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/RepositoryViewPart.java
@@ -0,0 +1,320 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.views;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.ICompoundModelElement;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.repository.IRepositoryModel;
+import org.cauldron.sigil.model.util.ModelHelper;
+import org.cauldron.sigil.repository.IBundleRepository;
+import org.cauldron.sigil.repository.IRepositoryChangeListener;
+import org.cauldron.sigil.repository.IRepositoryVisitor;
+import org.cauldron.sigil.repository.RepositoryChangeEvent;
+import org.cauldron.sigil.ui.SigilUI;
+import org.cauldron.sigil.ui.util.DefaultTreeContentProvider;
+import org.cauldron.sigil.ui.util.ModelLabelProvider;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.IToolBarManager;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.util.LocalSelectionTransfer;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.dnd.DND;
+import org.eclipse.swt.dnd.DragSourceAdapter;
+import org.eclipse.swt.dnd.DragSourceEvent;
+import org.eclipse.swt.dnd.Transfer;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.ui.IActionBars;
+import org.eclipse.ui.part.ViewPart;
+
+public class RepositoryViewPart extends ViewPart implements IRepositoryChangeListener {
+
+ public class FindUsersAction extends Action {
+ @Override
+ public String getText() {
+ return "Find Uses";
+ }
+
+ @Override
+ public void run() {
+ ISelection s = treeViewer.getSelection();
+ if ( !s.isEmpty() ) {
+ IStructuredSelection sel = (IStructuredSelection) s;
+ IModelElement e = (IModelElement) sel.getFirstElement();
+ List<IModelElement> users = ModelHelper.findUsers(e);
+ String msg = null;
+ if ( users.isEmpty() ) {
+ msg = "No users of " + e;
+ }
+ else {
+ StringBuilder b = new StringBuilder();
+ for ( IModelElement u : users ) {
+ ISigilBundle bndl = u.getAncestor(ISigilBundle.class);
+ b.append( bndl );
+ b.append( "->" );
+ b.append( u );
+ b.append( "\n" );
+ }
+ msg = b.toString();
+ }
+ MessageDialog.openInformation(getViewSite().getShell(), "Information", msg );
+ }
+ }
+ }
+
+ class RepositoryAction extends Action {
+ final IBundleRepository rep;
+ final IRepositoryModel model;
+
+ public RepositoryAction(IBundleRepository rep) {
+ this.rep = rep;
+ this.model = SigilCore.getRepositoryConfiguration().findRepository(rep.getId());
+ }
+
+
+ @Override
+ public void run() {
+ treeViewer.setInput(rep);
+ createMenu();
+ }
+
+ @Override
+ public String getText() {
+ String name = model.getName();
+ if ( treeViewer.getInput() == rep ) {
+ name = "> " + name;
+ }
+ return name;
+ }
+
+ @Override
+ public ImageDescriptor getImageDescriptor() {
+ Image img = model.getType().getIcon();
+ if ( img == null ) {
+ return ImageDescriptor.createFromFile(RepositoryViewPart.class, "/icons/jars_obj.png");
+ }
+ else {
+ return ImageDescriptor.createFromImage(img);
+ }
+ }
+ }
+
+ class RefreshAction extends Action {
+ @Override
+ public void run() {
+ IBundleRepository rep = (IBundleRepository) treeViewer.getInput();
+ if ( rep != null ) {
+ rep.refresh();
+ treeViewer.refresh();
+ }
+ }
+
+ @Override
+ public String getText() {
+ return "Refresh";
+ }
+
+ @Override
+ public ImageDescriptor getImageDescriptor() {
+ return ImageDescriptor.createFromFile(RepositoryViewPart.class, "/icons/refreshBundle.png");
+ }
+
+ }
+
+ private TreeViewer treeViewer;
+
+ @Override
+ public void createPartControl(Composite parent) {
+ createBody(parent);
+ createMenu();
+ SigilCore.getGlobalRepositoryManager().addRepositoryChangeListener(this);
+ }
+
+ @Override
+ public void dispose() {
+ SigilCore.getGlobalRepositoryManager().removeRepositoryChangeListener(this);
+ super.dispose();
+ }
+
+ private void createMenu() {
+ createTopMenu();
+ createLocalMenu();
+ }
+
+ private void createLocalMenu() {
+ /*MenuManager menuMgr = new MenuManager();
+ menuMgr.add( new FindUsersAction() );
+ Menu menu = menuMgr.createContextMenu(treeViewer.getControl());
+
+ treeViewer.getControl().setMenu(menu);
+ getViewSite().registerContextMenu(menuMgr, treeViewer); */
+ IActionBars bars = getViewSite().getActionBars();
+ IToolBarManager toolBar = bars.getToolBarManager();
+ toolBar.add( new RefreshAction() );
+ }
+
+ private void createTopMenu() {
+ IActionBars bars = getViewSite().getActionBars();
+ IMenuManager menu = bars.getMenuManager();
+ menu.removeAll();
+ for ( final IBundleRepository rep : SigilCore.getGlobalRepositoryManager().getRepositories() ) {
+ if ( treeViewer.getInput() == null ) {
+ treeViewer.setInput(rep);
+ }
+
+ RepositoryAction action = new RepositoryAction(rep);
+ menu.add( action );
+ }
+ }
+
+ private void createBody(Composite parent) {
+ // components
+ Composite control = new Composite(parent, SWT.NONE);
+ Tree tree = new Tree(control, SWT.NONE);
+
+ // layout
+ control.setLayout( new GridLayout(1, false ) );
+ tree.setLayoutData( new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1) );
+
+ // viewer
+ treeViewer = new TreeViewer(tree);
+ treeViewer.setContentProvider( new DefaultTreeContentProvider() {
+ public Object[] getChildren(Object parentElement) {
+ if ( parentElement instanceof ICompoundModelElement ) {
+ ICompoundModelElement model = (ICompoundModelElement) parentElement;
+ return model.children();
+ }
+
+ return null;
+ }
+
+ public Object getParent(Object element) {
+ if ( element instanceof IModelElement ) {
+ IModelElement model = (IModelElement) element;
+ return model.getParent();
+ }
+
+ return null;
+ }
+
+ public boolean hasChildren(Object element) {
+ if ( element instanceof ICompoundModelElement ) {
+ ICompoundModelElement model = (ICompoundModelElement) element;
+ return model.children().length > 0;
+ }
+ return false;
+ }
+
+ public Object[] getElements(Object inputElement) {
+ IBundleRepository rep = (IBundleRepository) inputElement;
+ return getBundles(rep);
+ }
+ });
+
+ treeViewer.setComparator( new ModelElementComparator() );
+
+ treeViewer.setLabelProvider( new ModelLabelProvider() );
+
+ treeViewer.addDragSupport(DND.DROP_LINK, new Transfer[] { LocalSelectionTransfer.getTransfer() }, new DragSourceAdapter() {
+ @Override
+ public void dragFinished(DragSourceEvent event) {
+ // TODO Auto-generated method stub
+ super.dragFinished(event);
+ }
+
+ @Override
+ public void dragSetData(DragSourceEvent event) {
+ // TODO Auto-generated method stub
+ super.dragSetData(event);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void dragStart(DragSourceEvent event) {
+ if ( treeViewer.getSelection().isEmpty() ) {
+ IStructuredSelection sel = (IStructuredSelection) treeViewer.getSelection();
+ for ( Iterator<IModelElement> i = sel.iterator(); i.hasNext(); ) {
+ IModelElement e = i.next();
+ if ( e instanceof ISigilBundle ) {
+ event.data = e;
+ }
+ else {
+ event.doit = false;
+ }
+ }
+ }
+ else {
+ event.doit = false;
+ }
+ }
+ });
+ }
+
+ @Override
+ public void setFocus() {
+ }
+
+ public void repositoryChanged(RepositoryChangeEvent event) {
+ switch ( event.getType() ) {
+ case ADDED:
+ createTopMenu();
+ break;
+ case CHANGED:
+ if ( event.getRepository() == treeViewer.getInput() ) {
+ SigilUI.runInUI( new Runnable() {
+ public void run() {
+ treeViewer.refresh();
+ }
+ } );
+ }
+ break;
+ case REMOVED:
+ if ( event.getRepository() == treeViewer.getInput() ) {
+ treeViewer.setInput(null);
+ }
+ createTopMenu();
+ }
+ }
+
+ private Object[] getBundles(IBundleRepository repository) {
+ final LinkedList<ISigilBundle> bundles = new LinkedList<ISigilBundle>();
+ repository.accept(new IRepositoryVisitor() {
+ public boolean visit(ISigilBundle bundle) {
+ bundles.add(bundle);
+ return true;
+ }
+ });
+ return bundles.toArray();
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleConnectionHighlighter.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleConnectionHighlighter.java
new file mode 100644
index 0000000..586375e
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleConnectionHighlighter.java
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.views.resolution;
+
+import java.util.Set;
+
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.eclipse.draw2d.ColorConstants;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.zest.core.widgets.GraphConnection;
+import org.eclipse.zest.core.widgets.GraphItem;
+import org.eclipse.zest.core.widgets.GraphNode;
+
+public class BundleConnectionHighlighter implements ISelectionChangedListener {
+
+ private BundleResolverView view;
+
+ public BundleConnectionHighlighter(BundleResolverView view) {
+ this.view = view;
+ }
+
+ public void selectionChanged(SelectionChangedEvent event) {
+ ISelection selection = event.getSelection();
+ if ( !selection.isEmpty() ) {
+ IStructuredSelection str = (IStructuredSelection) selection;
+
+ Object sel = str.getFirstElement();
+
+ if ( sel instanceof ISigilBundle ) {
+ BundleGraph graph = (BundleGraph) view.getBundlegraph();
+
+ ISigilBundle selected = (ISigilBundle) sel;
+ Set<ISigilBundle> connected = graph.getTargets(selected);
+
+ highlightLinks(graph, selected, connected);
+ highlightBundles(graph, selected, connected);
+ }
+ else if ( sel instanceof Link ) {
+ GraphConnection c = (GraphConnection) findGraphItem(sel);
+ if ( c != null ) {
+ c.unhighlight();
+ c.setHighlightColor(ColorConstants.blue);
+ c.highlight();
+ }
+ }
+ }
+ else {
+ // TODO clear highlights...
+ }
+ }
+
+ private void highlightBundles(BundleGraph graph, ISigilBundle selected, Set<ISigilBundle> connected) {
+ for ( ISigilBundle bundle : graph.getBundles() ) {
+ GraphNode node = (GraphNode) findGraphItem(bundle);
+ if ( node != null ) {
+ node.unhighlight();
+
+ if ( bundle == selected ) {
+ node.setHighlightColor(ColorConstants.yellow);
+ node.highlight();
+ }
+ else if ( view.isDisplayed(BundleResolverView.DEPENDENTS) ) {
+ if ( connected.contains(bundle) ) {
+ node.setHighlightColor(ColorConstants.lightBlue);
+ node.highlight();
+ }
+ }
+ }
+ }
+ }
+
+ private void highlightLinks(BundleGraph graph, ISigilBundle selected, Set<ISigilBundle> connected) {
+ for ( Link l : graph.getLinks() ) {
+ GraphConnection c = (GraphConnection) findGraphItem(l);
+ if ( c != null ) {
+ c.unhighlight();
+
+ if ( view.isDisplayed(BundleResolverView.DEPENDENTS) ) {
+ if ( l.getSource() == selected && connected.contains( l.getTarget() ) ) {
+ c.setHighlightColor(ColorConstants.lightBlue);
+ c.highlight();
+ }
+ }
+ }
+ }
+ }
+
+ private GraphItem findGraphItem(Object l) {
+ try {
+ return view.getGraphViewer().findGraphItem(l);
+ }
+ catch (ArrayIndexOutOfBoundsException e) {
+ // temporary fix for issue
+ // https://bugs.eclipse.org/bugs/show_bug.cgi?id=242523
+ return null;
+ }
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleGraph.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleGraph.java
new file mode 100644
index 0000000..8fad30f
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleGraph.java
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.views.resolution;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+
+public class BundleGraph {
+
+ private HashMap<ISigilBundle, LinkedList<Link>> lookup = new HashMap<ISigilBundle, LinkedList<Link>>();
+ private LinkedList<Link> links = new LinkedList<Link>();
+ private HashSet<ISigilBundle> bundles = new HashSet<ISigilBundle>();
+
+ public void startResolution(IModelElement requirement) {
+ }
+
+ public void endResolution(IModelElement requirement, ISigilBundle target) {
+ ISigilBundle source = requirement.getAncestor(ISigilBundle.class);
+
+ bundles.add(source);
+ bundles.add(target);
+
+ LinkedList<Link> links = lookup.get(source);
+
+ if ( links == null ) {
+ links = new LinkedList<Link>();
+ lookup.put(source, links);
+ }
+
+ Link l = null;
+ for ( Link c : links ) {
+ if ( c.getTarget() == target ) {
+ l = c;
+ break;
+ }
+ }
+
+ if ( l == null ) {
+ l = new Link(source, target);
+ links.add(l);
+ this.links.add(l);
+ }
+
+ l.addRequirement(requirement);
+ }
+
+ public List<Link> getLinks() {
+ return links;
+ }
+
+ public Set<ISigilBundle> getBundles() {
+ return bundles;
+ }
+
+ public Set<ISigilBundle> getTargets(ISigilBundle bundle) {
+ HashSet<ISigilBundle> targets = new HashSet<ISigilBundle>();
+
+ for ( Link l : getLinks(bundle) ) {
+ targets.add(l.getTarget());
+ }
+
+ return targets;
+ }
+
+ public List<Link> getLinks(ISigilBundle selected) {
+ List<Link> l = lookup.get(selected);
+ return l == null ? Collections.<Link>emptyList() : l;
+ }
+
+ public List<Link> getDependentLinks(ISigilBundle bundle) {
+ ArrayList<Link> found = new ArrayList<Link>(links.size());
+
+ for (Link l : links) {
+ if ( l.getTarget() == bundle ) {
+ found.add( l );
+ }
+ }
+
+ found.trimToSize();
+
+ return found;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleGraphContentProvider.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleGraphContentProvider.java
new file mode 100644
index 0000000..0c0547c
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleGraphContentProvider.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.views.resolution;
+
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.zest.core.viewers.IGraphContentProvider;
+
+public class BundleGraphContentProvider implements IGraphContentProvider {
+
+ public Object[] getElements(Object input) {
+ BundleGraph graph = (BundleGraph) input;
+ return graph.getLinks().toArray();
+ }
+
+ public Object getDestination(Object element) {
+ Link l = (Link) element;
+ return l.isSatisfied() ? l.getTarget() : new Link.Unsatisfied();
+ }
+
+ public Object getSource(Object element) {
+ Link l = (Link) element;
+ return l.getSource();
+ }
+
+ public void dispose() {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleGraphLabelProvider.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleGraphLabelProvider.java
new file mode 100644
index 0000000..d48c253
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleGraphLabelProvider.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.views.resolution;
+
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.ui.SigilUI;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.swt.graphics.Image;
+
+public class BundleGraphLabelProvider extends LabelProvider {
+
+ private BundleResolverView view;
+
+ public BundleGraphLabelProvider(BundleResolverView view) {
+ this.view = view;
+ }
+
+ @Override
+ public String getText(Object element) {
+ if ( element instanceof Link ) {
+ Link l = (Link) element;
+ if ( l.isSatisfied() ) {
+ if ( view.isDisplayed(BundleResolverView.LINK_LABELS) ) {
+ return view.getLinkText((Link) element);
+ }
+ else {
+ return "";
+ }
+ }
+ else {
+ return view.getLinkText((Link) element);
+ }
+ }
+ else if ( element instanceof ISigilBundle ) {
+ ISigilBundle b = (ISigilBundle) element;
+ return b.getBundleInfo().getSymbolicName() + ": " + b.getBundleInfo().getVersion();
+ }
+ else if ( element instanceof Link.Unsatisfied ) {
+ return "unsatisfied";
+ }
+ else {
+ return "unknown:" + element;
+ }
+ }
+
+ @Override
+ public Image getImage(Object element) {
+ Image result = null;
+ if ( element instanceof ISigilBundle ) {
+ result = SigilUI.cacheImage("icons/jar_obj.png", BundleGraphLabelProvider.class.getClassLoader());
+ }
+ else if ( element instanceof Link.Unsatisfied ) {
+ result = SigilUI.cacheImage("icons/error.gif", BundleGraphLabelProvider.class.getClassLoader());
+ }
+
+ return result;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleGraphViewFilter.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleGraphViewFilter.java
new file mode 100644
index 0000000..4100ad8
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleGraphViewFilter.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.views.resolution;
+
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerFilter;
+
+public class BundleGraphViewFilter extends ViewerFilter {
+
+ private BundleResolverView view;
+
+ public BundleGraphViewFilter(BundleResolverView view) {
+ this.view = view;
+ }
+
+ @Override
+ public boolean select(Viewer viewer, Object parentElement, Object element) {
+ if ( !view.isDisplayed(BundleResolverView.LOCAL_LINKS) ) {
+ if ( element instanceof Link ) {
+ Link l = (Link) element;
+ return l.getSource() != l.getTarget();
+ }
+ }
+ if ( !view.isDisplayed(BundleResolverView.SATISFIED) ) {
+ if ( element instanceof Link ) {
+ Link l = (Link) element;
+ return !l.isSatisfied();
+ }
+ else if ( element instanceof ISigilBundle ) {
+ ISigilBundle bundle = (ISigilBundle) element;
+ for ( Link l : view.getBundlegraph().getLinks(bundle)) {
+ if ( !l.isSatisfied() ) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ if ( !view.isDisplayed(BundleResolverView.UNSATISFIED) ) {
+ if ( element instanceof Link ) {
+ Link l = (Link) element;
+ return l.isSatisfied();
+ }
+ else if ( element instanceof Link.Unsatisfied ) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleResolverView.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleResolverView.java
new file mode 100644
index 0000000..91c0dcb
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/BundleResolverView.java
@@ -0,0 +1,322 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.views.resolution;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.cauldron.sigil.repository.IRepositoryManager;
+import org.cauldron.sigil.repository.IResolutionMonitor;
+import org.cauldron.sigil.repository.ResolutionConfig;
+import org.cauldron.sigil.repository.ResolutionException;
+import org.cauldron.sigil.repository.ResolutionMonitorAdapter;
+import org.cauldron.sigil.ui.SigilUI;
+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.core.runtime.jobs.Job;
+import org.eclipse.draw2d.IFigure;
+import org.eclipse.draw2d.Label;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.viewers.IContentProvider;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ControlAdapter;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.layout.FillLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.ui.IActionBars;
+import org.eclipse.ui.IPartService;
+import org.eclipse.ui.part.ViewPart;
+import org.eclipse.zest.core.viewers.GraphViewer;
+import org.eclipse.zest.core.widgets.Graph;
+import org.eclipse.zest.core.widgets.GraphConnection;
+import org.eclipse.zest.layouts.LayoutStyles;
+import org.eclipse.zest.layouts.algorithms.RadialLayoutAlgorithm;
+import org.eclipse.zest.layouts.algorithms.TreeLayoutAlgorithm;
+
+public class BundleResolverView extends ViewPart {
+
+ private static final String SHOW_LINK_LABELS = "Show link labels";
+ private static final String HIDE_LINK_LABELS = "Hide link labels";
+ private static final String SHOW_LOCAL_LINKS = "Show local links";
+ private static final String HIDE_LOCAL_LINKS = "Hide local links";
+ private static final String SHOW_DEPENDENTS = "Show dependents";
+ private static final String HIDE_DEPENDENTS = "Hide dependents";
+ private static final String SHOW_SATISFIED = "Show satisfied bundles";
+ private static final String HIDE_SATISFIED = "Hide satisfied bundles";
+ private static final String SHOW_UNSATISFIED = "Show unsatisfied bundles";
+ private static final String HIDE_UNSATISFIED = "Hide unsatisfied bundles";
+ private static final String SHOW_OPTIONAL = "Show optional dependencies";
+ private static final String HIDE_OPTIONAL = "Hide optional dependencies";
+
+ public static final String LINK_LABELS = "link.labels";
+ public static final String LOCAL_LINKS = "local.links";
+ public static final String DEPENDENTS = "dependents";
+ public static final String SATISFIED = "satisified";
+ public static final String UNSATISFIED = "unsatisfied";
+ public static final String OPTIONAL = "optional";
+
+ private GraphViewer viewer;
+ private IModelElement current;
+ private Job job;
+ private int lastX;
+ private int lastY;
+
+ private Map<String, Boolean> displayed = new HashMap<String, Boolean>();
+
+ private class ToggleDisplayAction extends Action {
+ private String key;
+ private String showMsg;
+ private String hideMsg;
+
+ ToggleDisplayAction(String key, String showMsg, String hideMsg) {
+ this.key = key;
+ this.showMsg = showMsg;
+ this.hideMsg = hideMsg;
+ setText(BundleResolverView.this.isDisplayed(key) ? hideMsg : showMsg);
+ }
+
+ @Override
+ public void run() {
+ BundleResolverView.this.setDisplayed( key, !BundleResolverView.this.isDisplayed(key) );
+ setText(BundleResolverView.this.isDisplayed(key) ? hideMsg : showMsg);
+ }
+ }
+
+ public void setInput(final IModelElement element) {
+ if ( current == null || !current.equals(element) ) {
+ SigilCore.log( "Set input " + element );
+ current = element;
+ redraw();
+ }
+ }
+
+ public void setDisplayed(String key, boolean b) {
+ displayed.put(key, b);
+
+ if ( key == DEPENDENTS ) {
+ int style = LayoutStyles.NO_LAYOUT_NODE_RESIZING;
+ viewer.setLayoutAlgorithm( b ? new TreeLayoutAlgorithm(style) : new RadialLayoutAlgorithm(style));
+ redraw();
+ }
+ else if ( key == OPTIONAL ) {
+ redraw();
+ }
+ else if ( key == SATISFIED || key == UNSATISFIED ) {
+ viewer.refresh();
+ }
+ }
+
+ public boolean isDisplayed(String key) {
+ return displayed.get(key);
+ }
+
+ @Override
+ public void setFocus() {
+ }
+
+ @Override
+ public void createPartControl( Composite parent ) {
+ init();
+ createViewer(parent);
+ createListeners();
+ createMenu();
+ }
+
+ private void init() {
+ displayed.put(LINK_LABELS, false);
+ displayed.put(LOCAL_LINKS, false);
+ displayed.put(DEPENDENTS, false);
+ displayed.put(OPTIONAL, false);
+ displayed.put(SATISFIED, true);
+ displayed.put(UNSATISFIED, true);
+ }
+
+ public BundleGraph getBundlegraph() {
+ return (BundleGraph) viewer.getInput();
+ }
+
+ GraphViewer getGraphViewer() {
+ return viewer;
+ }
+
+ String getLinkText(Link link) {
+ StringBuffer buf = new StringBuffer();
+
+ for ( IModelElement e : link.getRequirements() ) {
+ if ( buf.length() > 0 ) {
+ buf.append( "\n" );
+ }
+ if ( e instanceof IPackageImport ) {
+ IPackageImport pi = (IPackageImport) e;
+ buf.append( "import " + pi.getPackageName() + " : " + pi.getVersions() + ": " + (pi.isOptional() ? "optional" : "mandatory" ) );
+ }
+ else if ( e instanceof IRequiredBundle ) {
+ IRequiredBundle rb = (IRequiredBundle) e;
+ buf.append( "required bundle " + rb.getSymbolicName() + " : " + rb.getVersions() + ": " + (rb.isOptional() ? "optional" : "mandatory" ) );
+ }
+ }
+
+ return buf.toString();
+ }
+
+ private synchronized void redraw() {
+ final IModelElement element = current;
+ if ( job != null ) {
+ job.cancel();
+ }
+
+ job = new Job("Resolving " + current) {
+ @Override
+ protected IStatus run(IProgressMonitor progress) {
+ try {
+ resolve(element, progress);
+ return Status.OK_STATUS;
+ } catch (CoreException e) {
+ return e.getStatus();
+ }
+ }
+ };
+ job.schedule();
+ }
+
+ private void resolve(IModelElement element, IProgressMonitor progress) throws CoreException {
+ final BundleGraph graph = new BundleGraph();
+
+ IResolutionMonitor monitor = new ResolutionMonitorAdapter(progress) {
+ @Override
+ public void startResolution(IModelElement requirement) {
+ graph.startResolution(requirement);
+ }
+
+ @Override
+ public void endResolution(IModelElement requirement, ISigilBundle provider) {
+ graph.endResolution(requirement, provider);
+ }
+ };
+
+ ISigilProjectModel project = findProject(element);
+ IRepositoryManager repository = SigilCore.getRepositoryManager(project);
+
+ int options = ResolutionConfig.IGNORE_ERRORS;
+
+ if ( isDisplayed(DEPENDENTS) ) {
+ options |= ResolutionConfig.INCLUDE_DEPENDENTS;
+ }
+ if ( isDisplayed(OPTIONAL) ) {
+ options |= ResolutionConfig.INCLUDE_OPTIONAL;
+ }
+
+ ResolutionConfig config = new ResolutionConfig(options);
+
+ try {
+ repository.getBundleResolver().resolve(element, config, monitor);
+ } catch (ResolutionException e) {
+ throw SigilCore.newCoreException("Failed to resolve " + element, e);
+ }
+
+ SigilUI.runInUI( new Runnable() {
+ public void run() {
+ viewer.setInput(graph);
+ addCustomUIElements();
+ }
+ } );
+ }
+
+ private static ISigilProjectModel findProject(IModelElement element) {
+ if ( element == null ) {
+ return null;
+ }
+ if ( element instanceof ISigilProjectModel ) {
+ return (ISigilProjectModel) element;
+ }
+
+ return element.getAncestor(ISigilProjectModel.class);
+ }
+
+ @SuppressWarnings("unchecked")
+ private void addCustomUIElements() {
+ if ( !isDisplayed(LINK_LABELS) ) {
+ for ( GraphConnection c : (List<GraphConnection>) viewer.getGraphControl().getConnections() ) {
+ if ( c.getData() instanceof Link ) {
+ c.setTooltip(buildToolTip((Link) c.getData()));
+ }
+ }
+ }
+ }
+
+ private IFigure buildToolTip(Link link) {
+ Label l = new Label();
+ l.setText(getLinkText(link));
+ return l;
+ }
+
+ private void createViewer(Composite parent) {
+ parent.setLayout( new FillLayout() );
+ viewer = new GraphViewer(parent, SWT.H_SCROLL | SWT.V_SCROLL);
+ IContentProvider contentProvider = new BundleGraphContentProvider();
+ viewer.setContentProvider(contentProvider);
+ viewer.setLabelProvider(new BundleGraphLabelProvider(this));
+ viewer.addFilter( new BundleGraphViewFilter(this) );
+
+
+ int style = LayoutStyles.NO_LAYOUT_NODE_RESIZING;
+ viewer.setLayoutAlgorithm( isDisplayed(DEPENDENTS) ? new TreeLayoutAlgorithm(style) : new RadialLayoutAlgorithm(style));
+ viewer.addSelectionChangedListener( new BundleConnectionHighlighter(this) );
+ viewer.setInput(new BundleGraph());
+ }
+
+ private void createMenu() {
+ IActionBars action = getViewSite().getActionBars();
+ action.getMenuManager().add(new ToggleDisplayAction( LINK_LABELS, SHOW_LINK_LABELS, HIDE_LINK_LABELS ));
+ action.getMenuManager().add(new ToggleDisplayAction( LOCAL_LINKS, SHOW_LOCAL_LINKS, HIDE_LOCAL_LINKS ));
+ action.getMenuManager().add(new ToggleDisplayAction( DEPENDENTS, SHOW_DEPENDENTS, HIDE_DEPENDENTS ));
+ action.getMenuManager().add(new ToggleDisplayAction( OPTIONAL, SHOW_OPTIONAL, HIDE_OPTIONAL ));
+ action.getMenuManager().add(new ToggleDisplayAction( SATISFIED, SHOW_SATISFIED, HIDE_SATISFIED ));
+ action.getMenuManager().add(new ToggleDisplayAction( UNSATISFIED, SHOW_UNSATISFIED, HIDE_UNSATISFIED ));
+ action.updateActionBars();
+ }
+
+ private void createListeners() {
+ IPartService ps = (IPartService) getViewSite().getService(IPartService.class);
+ ps.addPartListener( new EditorViewPartListener(this) );
+ viewer.getGraphControl().addControlListener( new ControlAdapter() {
+ @Override
+ public void controlResized(ControlEvent e) {
+ Graph g = (Graph) e.getSource();
+ int x = g.getSize().x;
+ int y = g.getSize().y;
+ if ( lastX != x || lastY != y ) {
+ lastX = x;
+ lastY = y;
+ redraw();
+ }
+ }
+ } );
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/EditorViewPartListener.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/EditorViewPartListener.java
new file mode 100644
index 0000000..78bceb7
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/EditorViewPartListener.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.views.resolution;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IFileEditorInput;
+import org.eclipse.ui.IPartListener2;
+import org.eclipse.ui.IWorkbenchPartReference;
+
+public class EditorViewPartListener implements IPartListener2 {
+
+ private BundleResolverView bundleResolverView;
+
+ public EditorViewPartListener(BundleResolverView bundleResolverView) {
+ this.bundleResolverView = bundleResolverView;
+ }
+
+ public void partActivated(IWorkbenchPartReference partRef) {
+ checkRef(partRef);
+ }
+
+ public void partBroughtToTop(IWorkbenchPartReference partRef) {
+ // no action
+ }
+
+ public void partClosed(IWorkbenchPartReference partRef) {
+ // no action
+ }
+
+ public void partDeactivated(IWorkbenchPartReference partRef) {
+ // no action
+ }
+
+ public void partHidden(IWorkbenchPartReference partRef) {
+ // no action
+ }
+
+ public void partInputChanged(IWorkbenchPartReference partRef) {
+ // no action
+ }
+
+ public void partOpened(IWorkbenchPartReference partRef) {
+ // no action
+ }
+
+ public void partVisible(IWorkbenchPartReference partRef) {
+ // no action
+ }
+
+ private void checkRef(IWorkbenchPartReference partRef) {
+ IEditorPart editor = partRef.getPage().getActiveEditor();
+ if ( editor != null ) {
+ IEditorInput input = editor.getEditorInput();
+ if ( input instanceof IFileEditorInput ) {
+ IFileEditorInput f = (IFileEditorInput) input;
+ IProject project = f.getFile().getProject();
+ try {
+ ISigilProjectModel model = SigilCore.create(project);
+ if ( model != null ) {
+ bundleResolverView.setInput(model);
+ }
+ } catch (CoreException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/Link.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/Link.java
new file mode 100644
index 0000000..35591a4
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/views/resolution/Link.java
@@ -0,0 +1,138 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.views.resolution;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.cauldron.sigil.model.IModelElement;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.osgi.IPackageImport;
+import org.cauldron.sigil.model.osgi.IRequiredBundle;
+
+public class Link {
+ public static class Unsatisfied {
+
+ }
+
+ private ISigilBundle source;
+ private ISigilBundle target;
+
+ private LinkedList<IModelElement> requirements = new LinkedList<IModelElement>();
+ private static final Comparator<IModelElement> comparator = new Comparator<IModelElement>() {
+
+ public int compare(IModelElement o1, IModelElement o2) {
+ if (o1 instanceof IRequiredBundle) {
+ if ( o2 instanceof IRequiredBundle) {
+ return compareBundles( (IRequiredBundle) o1, (IRequiredBundle) o2 );
+ }
+ else {
+ return -1;
+ }
+ }
+ else {
+ if ( o2 instanceof IRequiredBundle ) {
+ return 1;
+ }
+ else {
+ return compareNonBundles( o1, o2 );
+ }
+ }
+ }
+
+ private int compareNonBundles(IModelElement o1, IModelElement o2) {
+ if (o1 instanceof IPackageImport) {
+ if ( o2 instanceof IPackageImport) {
+ return compareImports( (IPackageImport) o1, (IPackageImport) o2 );
+ }
+ else {
+ return -1;
+ }
+ }
+ else {
+ if ( o2 instanceof IPackageImport ) {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+ }
+ }
+
+ private int compareImports(IPackageImport o1, IPackageImport o2) {
+ return o1.getPackageName().compareTo( o2.getPackageName() );
+ }
+
+ private int compareBundles(IRequiredBundle o1, IRequiredBundle o2) {
+ return o1.getSymbolicName().compareTo( o2.getSymbolicName() );
+ }
+
+ };
+
+ public Link(ISigilBundle source, ISigilBundle target) {
+ this.source = source;
+ this.target = target;
+ }
+
+ public ISigilBundle getSource() {
+ return source;
+ }
+
+ public ISigilBundle getTarget() {
+ return target;
+ }
+
+ public boolean isSatisfied() {
+ return target != null;
+ }
+
+ public void addRequirement(IModelElement requirement) {
+ requirements.add(requirement);
+ Collections.sort(requirements, comparator);
+ }
+
+ public String toString() {
+ return "Link[" + source + "->" + target + "]";
+ }
+
+ public List<IModelElement> getRequirements() {
+ return requirements;
+ }
+
+ public boolean isOptional() {
+ for ( IModelElement e : requirements ) {
+ if ( e instanceof IPackageImport ) {
+ IPackageImport pi = (IPackageImport) e;
+ if ( !pi.isOptional() ) {
+ return false;
+ }
+ }
+ else if ( e instanceof IRequiredBundle ) {
+ IRequiredBundle rb = (IRequiredBundle) e;
+ if ( !rb.isOptional() ) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/SigilNewResourceWizard.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/SigilNewResourceWizard.java
new file mode 100644
index 0000000..bc4a39d
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/SigilNewResourceWizard.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.wizard;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.INewWizard;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.ide.IDE;
+import org.eclipse.ui.wizards.newresource.BasicNewResourceWizard;
+
+/**
+ * @author dave
+ *
+ */
+public abstract class SigilNewResourceWizard extends BasicNewResourceWizard implements INewWizard {
+
+ protected void selectRevealAndShow(IFile file) {
+ selectAndReveal(file);
+
+ // Open editor on new file.
+ IWorkbenchWindow dw = getWorkbench().getActiveWorkbenchWindow();
+ try {
+ if (dw != null) {
+ IWorkbenchPage page = dw.getActivePage();
+ if (page != null) {
+ IDE.openEditor(page, file, true);
+ }
+ }
+ }
+ catch (PartInitException e) {
+ MessageDialog.openError(Display.getCurrent().getActiveShell(), "Initialisation error",
+ "Failed to open " + file);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/WorkspaceContentProvider.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/WorkspaceContentProvider.java
new file mode 100644
index 0000000..cbab33b
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/WorkspaceContentProvider.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.wizard;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.cauldron.sigil.SigilCore;
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.Viewer;
+
+public class WorkspaceContentProvider implements ITreeContentProvider {
+
+ private final boolean includeNonSigil;
+ private final boolean includeClosed;
+
+ public WorkspaceContentProvider(boolean includeNonSigil, boolean includeClosed) {
+ this.includeNonSigil = includeNonSigil;
+ this.includeClosed = includeClosed;
+ }
+
+ public Object[] getChildren(Object parentElement) {
+ Object[] result = null;
+
+ if(parentElement instanceof IWorkspace) {
+ IProject[] projects = ((IWorkspace) parentElement).getRoot().getProjects();
+ if(includeNonSigil && includeClosed) {
+ result = projects;
+ } else {
+ List<IProject> includedProjects = new ArrayList<IProject>(projects.length);
+ for (IProject project : projects) {
+ if(!includeClosed && !project.isOpen()) {
+ continue;
+ }
+
+ if(!includeNonSigil) {
+ try {
+ if(project.getNature(SigilCore.NATURE_ID) == null) {
+ continue;
+ }
+ } catch (CoreException e) {
+ continue;
+ }
+ }
+
+ includedProjects.add(project);
+ }
+ result = includedProjects.toArray(new IProject[includedProjects.size()]);
+ }
+ } else if(parentElement instanceof IContainer) {
+ try {
+ IResource[] members = ((IContainer) parentElement).members();
+ List<IResource> children = new ArrayList<IResource>(members.length);
+ for (int i = 0; i < members.length; i++) {
+ if (members[i].getType() != IResource.FILE) {
+ children.add(members[i]);
+ }
+ }
+ result = children.toArray(new IResource[children.size()]);
+ } catch (CoreException e) {
+ // Shouldn't happen
+ }
+ }
+
+ return result;
+ }
+
+ public Object getParent(Object element) {
+ if(element instanceof IResource) {
+ return ((IResource) element).getParent();
+ }
+ return null;
+ }
+
+ public boolean hasChildren(Object element) {
+ return (element instanceof IContainer) && ((IContainer) element).isAccessible();
+ }
+
+ public Object[] getElements(Object inputElement) {
+ return getChildren(inputElement);
+ }
+
+ public void dispose() {
+ }
+
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/project/SigilProjectWizard.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/project/SigilProjectWizard.java
new file mode 100644
index 0000000..8f926bf
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/project/SigilProjectWizard.java
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.wizard.project;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.ui.wizard.SigilNewResourceWizard;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.resources.IWorkspaceRunnable;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExecutableExtension;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.wizards.newresource.BasicNewProjectResourceWizard;
+
+/**
+ * @author dave
+ *
+ */
+public class SigilProjectWizard extends SigilNewResourceWizard implements IExecutableExtension {
+
+ private SigilProjectWizardFirstPage firstPage;
+ private SigilProjectWizardSecondPage secondPage;
+
+ private String name;
+
+ public static final IPath SIGIL_PROJECT_PATH = new Path( SigilCore.SIGIL_PROJECT_FILE );
+ private IConfigurationElement config;
+
+ public void init(IWorkbench workbench, IStructuredSelection currentSelection) {
+ super.init(workbench, currentSelection);
+
+ firstPage = new SigilProjectWizardFirstPage();
+ firstPage.setInitialProjectName(name);
+ secondPage = new SigilProjectWizardSecondPage(firstPage);
+
+ addPage(firstPage);
+ addPage(secondPage);
+ }
+
+ private void finishPage(IProgressMonitor monitor) throws CoreException, InterruptedException {
+ secondPage.performFinish(monitor);
+
+ IProject newProject = firstPage.getProjectHandle();
+
+ if ( newProject != null && newProject.exists() ) {
+ IFile file = newProject.getFile( SigilProjectWizard.SIGIL_PROJECT_PATH );
+
+ selectRevealAndShow( file );
+
+ new Job("Check OSGi Install" ) {
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ // prompt for osgi home if not already set.
+ SigilCore.getInstallManager().getDefaultInstall();
+ return Status.OK_STATUS;
+ }
+ }.schedule();
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.wizard.Wizard#performFinish()
+ */
+ @Override
+ public boolean performFinish() {
+ IWorkspace workspace = ResourcesPlugin.getWorkspace();
+
+ IWorkspaceRunnable op= new IWorkspaceRunnable() {
+ public void run(IProgressMonitor monitor) throws CoreException {
+ try {
+ finishPage(monitor);
+ } catch (InterruptedException e) {
+ throw new OperationCanceledException(e.getMessage());
+ }
+ }
+ };
+
+ try {
+ workspace.run(op, Job.getJobManager().createProgressGroup());
+ }
+ catch (CoreException e) {
+ SigilCore.error( "Failed to complete project wizard", e);
+ return false;
+ }
+
+ BasicNewProjectResourceWizard.updatePerspective(config);
+ return true;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public boolean performCancel() {
+ secondPage.performCancel();
+ return super.performCancel();
+ }
+
+ public void setInitializationData(IConfigurationElement config, String propertyName, Object data)
+ throws CoreException {
+ this.config = config;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/project/SigilProjectWizardFirstPage.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/project/SigilProjectWizardFirstPage.java
new file mode 100644
index 0000000..78eaf19
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/project/SigilProjectWizardFirstPage.java
@@ -0,0 +1,171 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.wizard.project;
+
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.jface.fieldassist.ControlDecoration;
+import org.eclipse.jface.fieldassist.FieldDecoration;
+import org.eclipse.jface.fieldassist.FieldDecorationRegistry;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.dialogs.WizardNewProjectCreationPage;
+import org.osgi.framework.Version;
+
+/**
+ * @author dave
+ *
+ */
+public class SigilProjectWizardFirstPage extends WizardNewProjectCreationPage {
+
+ private volatile String description = "";
+ private volatile Version version = new Version(1, 0, 0);
+ private volatile String vendor = "";
+ private volatile String name = "";
+
+ private Text txtDescription;
+ private Text txtVersion;
+ private Text txtVendor;
+ private Text txtName;
+
+ public SigilProjectWizardFirstPage() {
+ super("newSigilProjectPage");
+ setTitle( "Sigil Project" );
+ setDescription( "Create a new Sigil project" );
+ }
+
+ public boolean isInWorkspace() {
+ IWorkspace workspace = ResourcesPlugin.getWorkspace();
+
+ IPath defaultDefaultLocation = workspace.getRoot().getLocation();
+
+ return defaultDefaultLocation.isPrefixOf( getLocationPath() );
+ }
+
+ @Override
+ public boolean isPageComplete() {
+ boolean result = super.isPageComplete();
+ return result;
+ }
+
+ @Override
+ public void createControl(Composite parent) {
+ FieldDecoration infoDecor = FieldDecorationRegistry.getDefault().getFieldDecoration(FieldDecorationRegistry.DEC_INFORMATION);
+
+ // Create controls
+ super.createControl(parent);
+ Composite control = (Composite) getControl();
+
+ Group grpProjectSettings = new Group(control, SWT.NONE);
+ grpProjectSettings.setText("Project Settings");
+
+ new Label(grpProjectSettings, SWT.NONE).setText("Version:");
+ txtVersion = new Text(grpProjectSettings, SWT.BORDER);
+
+ new Label(grpProjectSettings, SWT.NONE).setText("Name:");
+ txtName = new Text(grpProjectSettings, SWT.BORDER);
+
+ ControlDecoration txtNameDecor = new ControlDecoration(txtName, SWT.LEFT | SWT.CENTER);
+ txtNameDecor.setImage(infoDecor.getImage());
+ txtNameDecor.setDescriptionText("Defines a human-readable name for the bundle");
+
+ new Label(grpProjectSettings, SWT.NONE).setText("Description:");
+ txtDescription = new Text(grpProjectSettings, SWT.BORDER);
+
+ ControlDecoration txtDescDecor = new ControlDecoration(txtDescription, SWT.LEFT | SWT.CENTER);
+ txtDescDecor.setImage(infoDecor.getImage());
+ txtDescDecor.setDescriptionText("Defines a short human-readable description for the bundle");
+
+ new Label(grpProjectSettings, SWT.NONE).setText("Provider:");
+ txtVendor = new Text(grpProjectSettings, SWT.BORDER);
+
+ ControlDecoration txtVendorDecor = new ControlDecoration(txtVendor, SWT.LEFT | SWT.CENTER);
+ txtVendorDecor.setImage(infoDecor.getImage());
+ txtVendorDecor.setDescriptionText("The name of the company, organisation or individual providing the bundle");
+
+ // Set values
+ txtDescription.setText(description);
+ txtVersion.setText(version.toString());
+ txtVendor.setText(vendor);
+ txtName.setText(name);
+
+ // Add listeners
+ ModifyListener txtModListener = new ModifyListener() {
+ public void modifyText(ModifyEvent e) {
+ description = txtDescription.getText();
+ vendor = txtVendor.getText();
+ name = txtName.getText();
+
+ validateSettings();
+ }
+ };
+ txtDescription.addModifyListener(txtModListener);
+ txtVersion.addModifyListener(txtModListener);
+ txtVendor.addModifyListener(txtModListener);
+ txtName.addModifyListener(txtModListener);
+
+ // Layout
+ control.setLayout(new GridLayout());
+ grpProjectSettings.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ grpProjectSettings.setLayout(new GridLayout(2, false));
+ txtDescription.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ txtVersion.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ txtVendor.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ txtName.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+ }
+
+ private void validateSettings() {
+ try {
+ version = new Version(txtVersion.getText());
+ } catch (IllegalArgumentException e) {
+ version = null;
+ setErrorMessage("Invalid version");
+ setPageComplete(false);
+ return;
+ }
+
+ setErrorMessage(null);
+ setPageComplete(true);
+ }
+
+ public Version getVersion() {
+ return version;
+ }
+
+ public String getVendor() {
+ return vendor;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public String getName() {
+ return name;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/project/SigilProjectWizardSecondPage.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/project/SigilProjectWizardSecondPage.java
new file mode 100644
index 0000000..3b2592d
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/project/SigilProjectWizardSecondPage.java
@@ -0,0 +1,265 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.wizard.project;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.cauldron.sigil.SigilCore;
+import org.cauldron.sigil.model.eclipse.ISigilBundle;
+import org.cauldron.sigil.model.project.ISigilProjectModel;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.resources.IWorkspaceRunnable;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.SubProgressMonitor;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.ui.PreferenceConstants;
+import org.eclipse.jdt.ui.wizards.JavaCapabilityConfigurationPage;
+import org.osgi.framework.Version;
+
+/**
+ * @author dave
+ *
+ */
+public class SigilProjectWizardSecondPage extends JavaCapabilityConfigurationPage {
+
+ private SigilProjectWizardFirstPage firstPage;
+ private IProject currentProject;
+ private URI currentProjectLocation;
+ private boolean created;
+
+ public SigilProjectWizardSecondPage(SigilProjectWizardFirstPage firstPage) {
+ this.firstPage = firstPage;
+ }
+
+ @Override
+ public void setVisible(boolean visible) {
+ super.setVisible(visible);
+ if (visible) {
+ changeToNewProject();
+ } else {
+ removeProject();
+ }
+ }
+
+ @Override
+ protected boolean useNewSourcePage() {
+ return true;
+ }
+
+ protected void performFinish(IProgressMonitor monitor) throws CoreException, InterruptedException {
+ changeToNewProject();
+ updateProject(monitor);
+ }
+
+ private void changeToNewProject() {
+ if ( !created ) {
+ IWorkspace workspace = ResourcesPlugin.getWorkspace();
+
+ IWorkspaceRunnable op= new IWorkspaceRunnable() {
+ public void run(IProgressMonitor monitor) throws CoreException {
+ try {
+ updateProject(monitor);
+ } catch (InterruptedException e) {
+ throw new OperationCanceledException(e.getMessage());
+ }
+ }
+ };
+
+ try {
+ workspace.run(op, Job.getJobManager().createProgressGroup());
+ setErrorMessage(null);
+ setPageComplete(true);
+ created = true;
+ }
+ catch (CoreException e) {
+ SigilCore.error("Failed to run workspace job", e);
+ }
+ }
+ }
+
+ private void removeProject() {
+ if (currentProject == null || !currentProject.exists()) {
+ return;
+ }
+
+ IWorkspaceRunnable op= new IWorkspaceRunnable() {
+ public void run(IProgressMonitor monitor) throws CoreException {
+ doRemoveProject(monitor);
+ }
+ };
+
+ IWorkspace workspace = ResourcesPlugin.getWorkspace();
+
+ try {
+ workspace.run(op, Job.getJobManager().createProgressGroup());
+ }
+ catch (CoreException e) {
+ SigilCore.error("Failed to run workspace job", e);
+ }
+ finally {
+ created = false;
+ }
+ }
+
+ private void updateProject(IProgressMonitor monitor) throws CoreException, InterruptedException {
+ currentProject = firstPage.getProjectHandle();
+ currentProjectLocation= getProjectLocationURI();
+
+ String description = firstPage.getDescription();
+ Version projectVersion = firstPage.getVersion();
+ String vendor = firstPage.getVendor();
+ String name = firstPage.getName();
+
+ createProject( currentProject, currentProjectLocation, monitor);
+
+ IPath src = createSourcePath();
+
+ IPath output = getOutputLocation();
+
+ if ( output.segmentCount() == 0 ) {
+ output = new Path( currentProject.getName() ).append( "build" ).append( "classes" );
+ }
+
+ IClasspathEntry[] entries = getProjectClassPath(src);
+
+ SigilCore.makeSigilProject(currentProject, monitor);
+
+ init(JavaCore.create(currentProject), output.makeRelative(), entries, false);
+
+ configureJavaProject(new SubProgressMonitor(monitor, 3));
+
+ configureSigilProject( currentProject, description, projectVersion, vendor, name, src, monitor );
+ }
+
+ private IPath createSourcePath() throws CoreException {
+ IPath projectPath = currentProject.getFullPath();
+ IPath src = new Path( "src" );
+ IFolder f = currentProject.getFolder( src );
+ if ( !f.getLocation().toFile().exists() ) {
+ f.create(true, true, null);
+ }
+
+ return projectPath.append(src);
+ }
+
+ final void doRemoveProject(IProgressMonitor monitor) throws CoreException {
+ final boolean noProgressMonitor= (currentProjectLocation == null); // inside workspace
+
+ if (monitor == null || noProgressMonitor) {
+ monitor= new NullProgressMonitor();
+ }
+ monitor.beginTask("Remove project", 3);
+ try {
+ try {
+ boolean removeContent= currentProject.isSynchronized(IResource.DEPTH_INFINITE);
+ currentProject.delete(removeContent, false, new SubProgressMonitor(monitor, 2));
+
+ } finally {
+ }
+ } finally {
+ monitor.done();
+ currentProject= null;
+ }
+ }
+
+ private IClasspathEntry[] getProjectClassPath(IPath src) throws CoreException {
+ List<IClasspathEntry> cpEntries= new ArrayList<IClasspathEntry>();
+ cpEntries.add(JavaCore.newSourceEntry(src));
+ cpEntries.addAll(Arrays.asList(getDefaultClasspathEntry()));
+ cpEntries.add(JavaCore.newContainerEntry(new Path(SigilCore.CLASSPATH_CONTAINER_PATH)));
+ IClasspathEntry[] entries= cpEntries.toArray(new IClasspathEntry[cpEntries.size()]);
+
+ return entries;
+ }
+
+ private IClasspathEntry[] getDefaultClasspathEntry() {
+ IClasspathEntry[] defaultJRELibrary= PreferenceConstants.getDefaultJRELibrary();
+ /*String compliance= firstPage.getCompilerCompliance();
+ IPath jreContainerPath= new Path(JavaRuntime.JRE_CONTAINER);
+ if (compliance == null || defaultJRELibrary.length > 1 || !jreContainerPath.isPrefixOf(defaultJRELibrary[0].getPath())) {
+ // use default
+ return defaultJRELibrary;
+ }
+ IVMInstall inst= firstPage.getJVM();
+ if (inst != null) {
+ IPath newPath= jreContainerPath.append(inst.getVMInstallType().getId()).append(inst.getName());
+ return new IClasspathEntry[] { JavaCore.newContainerEntry(newPath) };
+ }*/
+ return defaultJRELibrary;
+ }
+
+
+ private void configureSigilProject( IProject project, String description, Version projectVersion, String vendorName, String bundleName, IPath src, IProgressMonitor monitor ) throws CoreException {
+ ISigilProjectModel sigil = SigilCore.create(project);
+ IClasspathEntry cp = JavaCore.newSourceEntry(src);
+ String encodedClasspath = sigil.getJavaModel().encodeClasspathEntry(cp );
+
+ ISigilBundle bundle = sigil.getBundle();
+ bundle.addClasspathEntry(encodedClasspath);
+
+ if(description != null) {
+ bundle.getBundleInfo().setDescription(description);
+ }
+ if(projectVersion != null) {
+ bundle.setVersion(projectVersion);
+ }
+ if(vendorName != null) {
+ bundle.getBundleInfo().setVendor(vendorName);
+ }
+ if(bundleName != null) {
+ bundle.getBundleInfo().setName(bundleName);
+ }
+ sigil.save(monitor);
+ }
+
+
+ private URI getProjectLocationURI() throws CoreException {
+ if (firstPage.isInWorkspace()) {
+ return null;
+ }
+ return firstPage.getLocationURI();
+ }
+
+ @Override
+ public boolean isPageComplete() {
+ boolean result = super.isPageComplete();
+ return result;
+ }
+
+ protected void performCancel() {
+ if(currentProject != null) {
+ removeProject();
+ }
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/repository/RepositoryWizard.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/repository/RepositoryWizard.java
new file mode 100644
index 0000000..c2403e9
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/repository/RepositoryWizard.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.wizard.repository;
+
+import org.cauldron.sigil.model.repository.IRepositoryModel;
+import org.eclipse.jface.wizard.IWizardPage;
+import org.eclipse.jface.wizard.Wizard;
+
+public class RepositoryWizard extends Wizard {
+
+ private IRepositoryModel model;
+
+ @Override
+ public boolean performFinish() {
+ for ( IWizardPage page : getPages() ) {
+ if ( page instanceof RepositoryWizardPage ) {
+ RepositoryWizardPage rwp = (RepositoryWizardPage) page;
+ rwp.storeFields();
+ }
+ }
+ return true;
+ }
+
+ public IRepositoryModel getModel() {
+ return model;
+ }
+
+ public void init(IRepositoryModel model) {
+ this.model = model;
+ }
+}
diff --git a/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/repository/RepositoryWizardPage.java b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/repository/RepositoryWizardPage.java
new file mode 100644
index 0000000..9a22aa4
--- /dev/null
+++ b/sigil/org.cauldron.sigil.ui/src/org/cauldron/sigil/ui/wizard/repository/RepositoryWizardPage.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.cauldron.sigil.ui.wizard.repository;
+
+import java.util.ArrayList;
+
+import org.cauldron.sigil.model.repository.IRepositoryModel;
+import org.eclipse.jface.preference.FieldEditor;
+import org.eclipse.jface.preference.StringFieldEditor;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+
+public abstract class RepositoryWizardPage extends WizardPage {
+
+ private StringFieldEditor nameEditor;
+ private ArrayList<FieldEditor> editors = new ArrayList<FieldEditor>();
+ private RepositoryWizard wizard;
+
+ protected RepositoryWizardPage(String pageName, RepositoryWizard parent) {
+ super(pageName);
+ setTitle(pageName);
+ this.wizard = parent;
+ }
+
+ public abstract void createFieldEditors();
+
+ public void addField(FieldEditor editor) {
+ editors.add( editor );
+ }
+
+ public void createControl(Composite parent) {
+ Composite control = new Composite(parent, SWT.NONE);
+ setControl(control);
+
+ if ( getModel().getType().isDynamic() ) {
+ nameEditor = new StringFieldEditor("name", "Name:", control);
+ nameEditor.setStringValue(getModel().getName());
+ nameEditor.getTextControl(getFieldEditorParent()).addModifyListener( new ModifyListener() {
+ public void modifyText(ModifyEvent e) {
+ checkPageComplete();
+ }
+ });
+ }
+
+ createFieldEditors();
+
+ int cols = nameEditor == null ? 0 : nameEditor.getNumberOfControls();
+ for ( FieldEditor e : editors ) {
+ cols = Math.max(cols, e.getNumberOfControls());
+ }
+
+ control.setLayout( new GridLayout(cols, false) );
+
+ if ( nameEditor != null ) {
+ nameEditor.fillIntoGrid(getFieldEditorParent(), cols);
+ }
+
+ for ( FieldEditor e : editors ) {
+ e.fillIntoGrid(getFieldEditorParent(), cols);
+ e.setPreferenceStore(getModel().getPreferences());
+ e.load();
+ }
+
+ checkPageComplete();
+ }
+
+ protected void checkPageComplete() {
+ if ( nameEditor != null ) {
+ setPageComplete(nameEditor.getStringValue().length() > 0);
+ }
+ }
+
+ public IRepositoryModel getModel() {
+ return wizard.getModel();
+ }
+
+ protected Composite getFieldEditorParent() {
+ return (Composite) getControl();
+ }
+
+ public void storeFields() {
+ getModel().setName(nameEditor.getStringValue());
+ for ( FieldEditor e : editors ) {
+ e.store();
+ }
+ }
+
+}