Improve multi-layers and introduce documents layer.
authorMathieu Baudier <mbaudier@argeo.org>
Sat, 24 Oct 2020 11:05:11 +0000 (13:05 +0200)
committerMathieu Baudier <mbaudier@argeo.org>
Sat, 24 Oct 2020 11:05:11 +0000 (13:05 +0200)
26 files changed:
library/org.argeo.documents.ui/.project
library/org.argeo.documents.ui/OSGI-INF/documentsLayer.xml [new file with mode: 0644]
library/org.argeo.documents.ui/OSGI-INF/entryArea.xml [new file with mode: 0644]
library/org.argeo.documents.ui/bnd.bnd
library/org.argeo.documents.ui/build.properties
library/org.argeo.documents.ui/config/documentsLayer.properties [new file with mode: 0644]
library/org.argeo.documents.ui/config/entryArea.properties [new file with mode: 0644]
library/org.argeo.documents.ui/src/org/argeo/documents/ui/DocumentsFileComposite.java
library/org.argeo.documents.ui/src/org/argeo/documents/ui/DocumentsTreeUiProvider.java [new file with mode: 0644]
library/org.argeo.documents.ui/src/org/argeo/documents/ui/DocumentsUtils.java [deleted file]
org.argeo.suite.core/src/org/argeo/suite/RankedObject.java [new file with mode: 0644]
org.argeo.suite.ui/OSGI-INF/cmsApp.xml
org.argeo.suite.ui/OSGI-INF/dashboardLayer.xml [new file with mode: 0644]
org.argeo.suite.ui/OSGI-INF/leadPane.xml
org.argeo.suite.ui/bnd.bnd
org.argeo.suite.ui/build.properties
org.argeo.suite.ui/config/dashboardLayer.properties [new file with mode: 0644]
org.argeo.suite.ui/src/org/argeo/suite/ui/ArgeoSuiteApp.java [deleted file]
org.argeo.suite.ui/src/org/argeo/suite/ui/ArgeoSuiteUi.java [deleted file]
org.argeo.suite.ui/src/org/argeo/suite/ui/DefaultEditionLayer.java [new file with mode: 0644]
org.argeo.suite.ui/src/org/argeo/suite/ui/DefaultLeadPane.java
org.argeo.suite.ui/src/org/argeo/suite/ui/SuiteApp.java [new file with mode: 0644]
org.argeo.suite.ui/src/org/argeo/suite/ui/SuiteLayer.java [new file with mode: 0644]
org.argeo.suite.ui/src/org/argeo/suite/ui/SuiteUi.java [new file with mode: 0644]
org.argeo.suite.ui/src/org/argeo/suite/ui/WorkLayer.java [deleted file]
sdk/argeo-suite-rap.properties

index 27b103e47d06dff55ee965b82d2e336378116d19..c046a7d544091d70957487d4ac9805ecf31e05c8 100644 (file)
                        <arguments>
                        </arguments>
                </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.pde.ds.core.builder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
        </buildSpec>
        <natures>
                <nature>org.eclipse.pde.PluginNature</nature>
diff --git a/library/org.argeo.documents.ui/OSGI-INF/documentsLayer.xml b/library/org.argeo.documents.ui/OSGI-INF/documentsLayer.xml
new file mode 100644 (file)
index 0000000..3833237
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="Documents Layer">
+   <implementation class="org.argeo.suite.ui.DefaultEditionLayer"/>
+   <service>
+      <provide interface="org.argeo.suite.ui.SuiteLayer"/>
+   </service>
+   <reference bind="setEntryArea" cardinality="1..1" interface="org.argeo.cms.ui.CmsUiProvider" name="CmsUiProvider" policy="dynamic" target="(service.pid=argeo.documents.ui.entryArea)"/>
+   <properties entry="config/documentsLayer.properties"/>
+</scr:component>
diff --git a/library/org.argeo.documents.ui/OSGI-INF/entryArea.xml b/library/org.argeo.documents.ui/OSGI-INF/entryArea.xml
new file mode 100644 (file)
index 0000000..eed1520
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="Documents Entry Area">
+   <implementation class="org.argeo.documents.ui.DocumentsTreeUiProvider"/>
+   <service>
+      <provide interface="org.argeo.cms.ui.CmsUiProvider"/>
+   </service>
+   <reference bind="setNodeFileSystemProvider" cardinality="1..1" interface="java.nio.file.spi.FileSystemProvider" name="FileSystemProvider" policy="dynamic" target="(service.pid=org.argeo.api.fsProvider)"/>
+   <properties entry="config/entryArea.properties"/>
+</scr:component>
index 4ce2057008305285c2de7a6bb4095849a9bea109..cf460283fe488dbd8da4fd9fc0dd50fac68b05d1 100644 (file)
@@ -1,4 +1,9 @@
+Service-Component:\
+OSGI-INF/entryArea.xml,\
+OSGI-INF/documentsLayer.xml
+
 Import-Package:\
 org.eclipse.swt,\
 org.argeo.api,\
+org.argeo.suite.ui,\
 *
\ No newline at end of file
index 34d2e4d2dad529ceaeb953bfcdb63c51d69ffed2..6ac35f7846e80cc600cd5d58037143d0dbb67b05 100644 (file)
@@ -1,4 +1,6 @@
-source.. = src/
 output.. = bin/
 bin.includes = META-INF/,\
-               .
+               .,\
+               OSGI-INF/,\
+               OSGI-INF/documentsLayer.xml
+source.. = src/
diff --git a/library/org.argeo.documents.ui/config/documentsLayer.properties b/library/org.argeo.documents.ui/config/documentsLayer.properties
new file mode 100644 (file)
index 0000000..2906f2d
--- /dev/null
@@ -0,0 +1 @@
+service.pid=argeo.documents.ui.documentsLayer
diff --git a/library/org.argeo.documents.ui/config/entryArea.properties b/library/org.argeo.documents.ui/config/entryArea.properties
new file mode 100644 (file)
index 0000000..43b08f0
--- /dev/null
@@ -0,0 +1 @@
+service.pid=argeo.documents.ui.entryArea
index e0eaf1e6b6b11d48f0a5cdeb97f0fad46fd1fccf..84c56a3a537b64f45f209c6eeb350f1339df88a4 100644 (file)
@@ -10,6 +10,7 @@ import javax.jcr.RepositoryException;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.argeo.cms.fs.CmsFsUtils;
 import org.argeo.cms.ui.util.CmsUiUtils;
 import org.argeo.eclipse.ui.EclipseUiUtils;
 import org.argeo.eclipse.ui.fs.FsUiUtils;
@@ -48,7 +49,7 @@ public class DocumentsFileComposite extends Composite {
 
                rightPannelCmp = new Composite(form, SWT.NO_FOCUS);
 
-               Path path = DocumentsUtils.getPath(fsp, context);
+               Path path = CmsFsUtils.getPath(fsp, context);
                setOverviewInput(path);
                form.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
                form.setWeights(new int[] { 55, 20 });
diff --git a/library/org.argeo.documents.ui/src/org/argeo/documents/ui/DocumentsTreeUiProvider.java b/library/org.argeo.documents.ui/src/org/argeo/documents/ui/DocumentsTreeUiProvider.java
new file mode 100644 (file)
index 0000000..224576e
--- /dev/null
@@ -0,0 +1,40 @@
+package org.argeo.documents.ui;
+
+import java.nio.file.Path;
+import java.nio.file.spi.FileSystemProvider;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.argeo.api.NodeUtils;
+import org.argeo.cms.fs.CmsFsUtils;
+import org.argeo.cms.ui.CmsUiProvider;
+import org.argeo.cms.ui.util.CmsUiUtils;
+import org.argeo.eclipse.ui.fs.FsTreeViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+/** Tree view of a user root folders. */
+public class DocumentsTreeUiProvider implements CmsUiProvider {
+       private FileSystemProvider nodeFileSystemProvider;
+
+       @Override
+       public Control createUi(Composite parent, Node context) throws RepositoryException {
+               parent.setLayout(new GridLayout());
+               FsTreeViewer fsTreeViewer = new FsTreeViewer(parent, SWT.NONE);
+               fsTreeViewer.configureDefaultSingleColumnTable(500);
+               Node homeNode = NodeUtils.getUserHome(context.getSession());
+               Path homePath = CmsFsUtils.getPath(nodeFileSystemProvider, homeNode);
+               fsTreeViewer.setPathsInput(homePath);
+               fsTreeViewer.getControl().setLayoutData(CmsUiUtils.fillAll());
+               fsTreeViewer.getControl().getParent().layout(true, true);
+               return fsTreeViewer.getControl();
+       }
+
+       public void setNodeFileSystemProvider(FileSystemProvider nodeFileSystemProvider) {
+               this.nodeFileSystemProvider = nodeFileSystemProvider;
+       }
+
+}
diff --git a/library/org.argeo.documents.ui/src/org/argeo/documents/ui/DocumentsUtils.java b/library/org.argeo.documents.ui/src/org/argeo/documents/ui/DocumentsUtils.java
deleted file mode 100644 (file)
index ad103c6..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-package org.argeo.documents.ui;
-
-import java.io.IOException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.nio.file.FileSystem;
-import java.nio.file.Path;
-import java.nio.file.spi.FileSystemProvider;
-
-import javax.jcr.NoSuchWorkspaceException;
-import javax.jcr.Node;
-import javax.jcr.NodeIterator;
-import javax.jcr.Repository;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-import javax.jcr.query.Query;
-import javax.jcr.query.QueryManager;
-
-import org.argeo.api.NodeConstants;
-import org.argeo.jcr.Jcr;
-
-/** Utilities around documents. */
-public class DocumentsUtils {
-       // TODO make it more robust and configurable
-       private static String baseWorkspaceName = NodeConstants.SYS_WORKSPACE;
-
-       public static Node getNode(Repository repository, Path path) {
-               String workspaceName = path.getNameCount() == 0 ? baseWorkspaceName : path.getName(0).toString();
-               String jcrPath = '/' + path.subpath(1, path.getNameCount()).toString();
-               try {
-                       Session newSession;
-                       try {
-                               newSession = repository.login(workspaceName);
-                       } catch (NoSuchWorkspaceException e) {
-                               // base workspace
-                               newSession = repository.login(baseWorkspaceName);
-                               jcrPath = path.toString();
-                       }
-                       return newSession.getNode(jcrPath);
-               } catch (RepositoryException e) {
-                       throw new IllegalStateException("Cannot get node from path " + path, e);
-               }
-       }
-
-       public static NodeIterator getLastUpdatedDocuments(Session session) {
-               try {
-                       String qStr = "//element(*, nt:file)";
-                       qStr += " order by @jcr:lastModified descending";
-                       QueryManager queryManager = session.getWorkspace().getQueryManager();
-                       @SuppressWarnings("deprecation")
-                       Query xpathQuery = queryManager.createQuery(qStr, Query.XPATH);
-                       xpathQuery.setLimit(8);
-                       NodeIterator nit = xpathQuery.execute().getNodes();
-                       return nit;
-               } catch (RepositoryException e) {
-                       throw new IllegalStateException("Unable to retrieve last updated documents", e);
-               }
-       }
-
-       public static Path getPath(FileSystemProvider nodeFileSystemProvider, URI uri) {
-               try {
-                       FileSystem fileSystem = nodeFileSystemProvider.getFileSystem(uri);
-                       if (fileSystem == null)
-                               fileSystem = nodeFileSystemProvider.newFileSystem(uri, null);
-                       String path = uri.getPath();
-                       return fileSystem.getPath(path);
-               } catch (IOException e) {
-                       throw new IllegalStateException("Unable to initialise file system for " + uri, e);
-               }
-       }
-
-       public static Path getPath(FileSystemProvider nodeFileSystemProvider, Node node) {
-               String workspaceName = Jcr.getWorkspaceName(node);
-               String fullPath = baseWorkspaceName.equals(workspaceName) ? Jcr.getPath(node)
-                               : '/' + workspaceName + Jcr.getPath(node);
-               URI uri;
-               try {
-                       uri = new URI(NodeConstants.SCHEME_NODE, null, fullPath, null);
-               } catch (URISyntaxException e) {
-                       throw new IllegalArgumentException("Cannot interpret " + fullPath + " as an URI", e);
-               }
-               return getPath(nodeFileSystemProvider, uri);
-       }
-
-       /** Singleton. */
-       private DocumentsUtils() {
-       }
-}
diff --git a/org.argeo.suite.core/src/org/argeo/suite/RankedObject.java b/org.argeo.suite.core/src/org/argeo/suite/RankedObject.java
new file mode 100644 (file)
index 0000000..a4d2833
--- /dev/null
@@ -0,0 +1,98 @@
+package org.argeo.suite;
+
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * A container for an object whose relevance can be ranked. Typically used in an
+ * OSGi context with the service.ranking property.
+ */
+public class RankedObject<T> {
+       private final static Log log = LogFactory.getLog(RankedObject.class);
+
+       private final static String SERVICE_RANKING = "service.ranking";
+//     private final static String SERVICE_ID = "service.id";
+
+       private T object;
+       private Map<String, Object> properties;
+       private final Long rank;
+
+       public RankedObject(T object, Map<String, Object> properties) {
+               this(object, properties, extractRanking(properties));
+       }
+
+       public RankedObject(T object, Map<String, Object> properties, Long rank) {
+               super();
+               this.object = object;
+               this.properties = properties;
+               this.rank = rank;
+       }
+
+       private static Long extractRanking(Map<String, Object> properties) {
+               if (properties == null)
+                       return 0l;
+               if (properties.containsKey(SERVICE_RANKING))
+                       return ((Integer) properties.get(SERVICE_RANKING)).longValue();
+//             else if (properties.containsKey(SERVICE_ID))
+//                     return (Long) properties.get(SERVICE_ID);
+               else
+                       return 0l;
+       }
+
+       public T get() {
+               return object;
+       }
+
+       public Map<String, Object> getProperties() {
+               return properties;
+       }
+
+       public Long getRank() {
+               return rank;
+       }
+
+       @Override
+       public int hashCode() {
+               return object.hashCode();
+       }
+
+       @Override
+       public boolean equals(Object obj) {
+               if (!(obj instanceof RankedObject))
+                       return false;
+               RankedObject<?> other = (RankedObject<?>) obj;
+               return rank.equals(other.rank) && object.equals(other.object);
+       }
+
+       @Override
+       public String toString() {
+               return object.getClass().getName() + " with rank " + rank;
+       }
+
+       public static <K, T> RankedObject<T> putIfHigherRank(Map<K, RankedObject<T>> map, K key, T object,
+                       Map<String, Object> properties) {
+               RankedObject<T> rankedObject = new RankedObject<>(object, properties);
+               if (!map.containsKey(key)) {
+                       map.put(key, rankedObject);
+                       if (log.isDebugEnabled())
+                               log.debug(
+                                               "Added " + key + " as " + object.getClass().getName() + " with rank " + rankedObject.getRank());
+                       return rankedObject;
+               } else {
+                       RankedObject<T> current = map.get(key);
+                       if (current.getRank() <= rankedObject.getRank()) {
+                               map.put(key, rankedObject);
+                               if (log.isDebugEnabled())
+                                       log.debug("Replaced " + key + " by " + object.getClass().getName() + " with rank "
+                                                       + rankedObject.getRank());
+                               return rankedObject;
+                       } else {
+                               return current;
+                       }
+               }
+
+       }
+
+}
index 5a26f52d0b488d2856f68349ee12d502e09f5884..75584fca3b2c1c4f2e5fddc754aae4151ce08045 100644 (file)
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="init" deactivate="destroy" name="Argeo Suite App">
-   <implementation class="org.argeo.suite.ui.ArgeoSuiteApp"/>
+   <implementation class="org.argeo.suite.ui.SuiteApp"/>
    <service>
       <provide interface="org.argeo.cms.ui.CmsApp"/>
       <provide interface="org.osgi.service.event.EventHandler"/>
@@ -8,5 +8,6 @@
    <reference bind="addUiProvider" cardinality="0..n" interface="org.argeo.cms.ui.CmsUiProvider" name="CmsUiProvider" policy="dynamic" unbind="removeUiProvider"/>
    <reference bind="addTheme" cardinality="1..n" interface="org.argeo.cms.ui.CmsTheme" name="CmsTheme" policy="dynamic" unbind="removeTheme"/>
    <properties entry="config/cmsApp.properties"/>
-   <reference bind="setRepository" cardinality="1..1" interface="javax.jcr.Repository" name="Repository" policy="dynamic"/>
+   <reference bind="setRepository" cardinality="1..1" interface="javax.jcr.Repository" name="Repository" policy="dynamic" target="(cn=ego)"/>
+   <reference bind="addLayer" cardinality="1..n" interface="org.argeo.suite.ui.SuiteLayer" name="SuiteLayer" policy="dynamic" unbind="removeLayer"/>
 </scr:component>
diff --git a/org.argeo.suite.ui/OSGI-INF/dashboardLayer.xml b/org.argeo.suite.ui/OSGI-INF/dashboardLayer.xml
new file mode 100644 (file)
index 0000000..b60eafc
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="Dashboard Layer">
+   <implementation class="org.argeo.suite.ui.DefaultEditionLayer"/>
+   <service>
+      <provide interface="org.argeo.suite.ui.SuiteLayer"/>
+   </service>
+   <properties entry="config/dashboardLayer.properties"/>
+   <reference bind="setEntryArea" cardinality="1..1" interface="org.argeo.cms.ui.CmsUiProvider" name="CmsUiProvider" policy="dynamic" target="(service.pid=argeo.suite.ui.recentItems)"/>
+</scr:component>
index ebd348b9534fc8351ef230125c23809b2113b5cb..bcbc88b85989617e6efcc2827c87d7f0445b3913 100644 (file)
@@ -5,4 +5,7 @@
       <provide interface="org.argeo.cms.ui.CmsUiProvider"/>
    </service>
    <properties entry="config/leadPane.properties"/>
+   <property name="defaultLayers" type="String">argeo.suite.ui.dashboardLayer
+argeo.documents.ui.documentsLayer
+   </property>
 </scr:component>
index b8793ec7b23ad500840db4b55fa167e50ca446ed..b5c8daab5481d1cddc266005337d817d0e8a5f67 100644 (file)
@@ -4,7 +4,8 @@ OSGI-INF/header.xml,\
 OSGI-INF/leadPane.xml,\
 OSGI-INF/loginScreen.xml,\
 OSGI-INF/recentItems.xml,\
-OSGI-INF/dashboard.xml
+OSGI-INF/dashboard.xml,\
+OSGI-INF/dashboardLayer.xml
 
 Import-Package:\
 org.argeo.api,\
index 1a1bc1a2f3ceedefb5a38ee702c3dfebffb455de..d8299677b7a7540603baf015245a094181871182 100644 (file)
@@ -5,5 +5,6 @@ bin.includes = META-INF/,\
                config/,\
                OSGI-INF/loginScreen.xml,\
                OSGI-INF/dashboard.xml,\
-               OSGI-INF/recentItems.xml
+               OSGI-INF/recentItems.xml,\
+               OSGI-INF/dashboardLayer.xml
 source.. = src/
diff --git a/org.argeo.suite.ui/config/dashboardLayer.properties b/org.argeo.suite.ui/config/dashboardLayer.properties
new file mode 100644 (file)
index 0000000..c7b815b
--- /dev/null
@@ -0,0 +1 @@
+service.pid=argeo.suite.ui.dashboardLayer
\ No newline at end of file
diff --git a/org.argeo.suite.ui/src/org/argeo/suite/ui/ArgeoSuiteApp.java b/org.argeo.suite.ui/src/org/argeo/suite/ui/ArgeoSuiteApp.java
deleted file mode 100644 (file)
index db71387..0000000
+++ /dev/null
@@ -1,298 +0,0 @@
-package org.argeo.suite.ui;
-
-import static org.argeo.cms.ui.CmsView.CMS_VIEW_UID_PROPERTY;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.SortedMap;
-import java.util.TreeMap;
-
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.cms.ui.AbstractCmsApp;
-import org.argeo.cms.ui.CmsTheme;
-import org.argeo.cms.ui.CmsUiProvider;
-import org.argeo.cms.ui.CmsView;
-import org.argeo.cms.ui.dialogs.CmsFeedback;
-import org.argeo.cms.ui.util.CmsEvent;
-import org.argeo.cms.ui.util.CmsUiUtils;
-import org.argeo.entity.EntityNames;
-import org.argeo.entity.EntityTypes;
-import org.argeo.jcr.Jcr;
-import org.argeo.jcr.JcrUtils;
-import org.argeo.suite.RankingKey;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Control;
-import org.osgi.service.event.Event;
-import org.osgi.service.event.EventHandler;
-
-/** The Argeo Suite App. */
-public class ArgeoSuiteApp extends AbstractCmsApp implements EventHandler {
-       private final static Log log = LogFactory.getLog(ArgeoSuiteApp.class);
-
-       public final static String PID_PREFIX = "argeo.suite.ui.";
-       public final static String HEADER_PID = PID_PREFIX + "header";
-       public final static String LEAD_PANE_PID = PID_PREFIX + "leadPane";
-       public final static String LOGIN_SCREEN_PID = PID_PREFIX + "loginScreen";
-       public final static String DASHBOARD_PID = PID_PREFIX + "dashboard";
-       public final static String RECENT_ITEMS_PID = PID_PREFIX + "recentItems";
-
-       private final static String DEFAULT_UI_NAME = "app";
-       private final static String DEFAULT_THEME_ID = "org.argeo.suite.theme.default";
-
-       private SortedMap<RankingKey, CmsUiProvider> uiProviders = Collections.synchronizedSortedMap(new TreeMap<>());
-
-       // TODO make more optimal or via CmsSession/CmsView
-       private Map<String, ArgeoSuiteUi> managedUis = new HashMap<>();
-
-//     private CmsUiProvider headerPart = null;
-
-       public void init(Map<String, String> properties) {
-               if (log.isDebugEnabled())
-                       log.info("Argeo Suite App started");
-       }
-
-       public void destroy(Map<String, String> properties) {
-               for (ArgeoSuiteUi ui : managedUis.values())
-                       if (!ui.isDisposed())
-                               ui.dispose();
-               if (log.isDebugEnabled())
-                       log.info("Argeo Suite App stopped");
-
-       }
-
-       @Override
-       public Set<String> getUiNames() {
-               HashSet<String> uiNames = new HashSet<>();
-               uiNames.add(DEFAULT_UI_NAME);
-               return uiNames;
-       }
-
-       @Override
-       public Composite initUi(Composite parent) {
-               String uiName = parent.getData(UI_NAME_PROPERTY) != null ? parent.getData(UI_NAME_PROPERTY).toString() : null;
-               CmsView cmsView = CmsView.getCmsView(parent);
-               if (cmsView == null)
-                       throw new IllegalStateException("No CMS view is registered.");
-               CmsTheme theme = getTheme(uiName);
-               if (theme != null)
-                       CmsTheme.registerCmsTheme(parent.getShell(), theme);
-               ArgeoSuiteUi argeoSuiteUi = new ArgeoSuiteUi(parent, SWT.NONE);
-               String uid = cmsView.getUid();
-               managedUis.put(uid, argeoSuiteUi);
-               argeoSuiteUi.addDisposeListener((e) -> {
-                       managedUis.remove(uid);
-                       if (log.isDebugEnabled())
-                               log.debug("Suite UI " + uid + " has been disposed.");
-               });
-               refreshUi(argeoSuiteUi, null);
-               return argeoSuiteUi;
-       }
-
-       @Override
-       public String getThemeId(String uiName) {
-               // TODO make it configurable
-               return DEFAULT_THEME_ID;
-       }
-
-       @Override
-       public void refreshUi(Composite parent, String state) {
-               try {
-                       Node context = null;
-                       ArgeoSuiteUi ui = (ArgeoSuiteUi) parent;
-                       refreshPart(findUiProvider(HEADER_PID, context), ui.getHeader(), context);
-                       CmsView cmsView = CmsView.getCmsView(parent);
-                       if (cmsView.isAnonymous()) {
-                               ui.refreshBelowHeader(false);
-                               refreshPart(findUiProvider(LOGIN_SCREEN_PID, context), ui.getBelowHeader(), context);
-                       } else {
-                               try {
-                                       if (ui.getSession() == null)
-                                               ui.setSession(getRepository().login());
-                                       context = ui.getSession().getRootNode();
-
-                               } catch (RepositoryException e) {
-                                       e.printStackTrace();
-                               }
-                               ui.refreshBelowHeader(true);
-
-                               ui.addLayer(ArgeoSuiteUi.DASHBOARD_LAYER);
-                               ui.addLayer("documents");
-                               ui.addLayer("locations");
-                               ui.addLayer("people");
-                               ui.switchToLayer(ArgeoSuiteUi.DASHBOARD_LAYER);
-
-                               refreshPart(findUiProvider(DASHBOARD_PID, context), ui.getTabbedArea().getCurrent(), context);
-                               refreshPart(findUiProvider(LEAD_PANE_PID, context), ui.getLeadPane(), context);
-                               refreshPart(findUiProvider(RECENT_ITEMS_PID, context), ui.getEntryArea(), context);
-                       }
-                       ui.layout(true, true);
-               } catch (Exception e) {
-                       CmsFeedback.show("Unexpected exception", e);
-               }
-       }
-
-       private void refreshPart(CmsUiProvider uiProvider, Composite part, Node context) {
-               CmsUiUtils.clear(part);
-               uiProvider.createUiPart(part, context);
-       }
-
-       private CmsUiProvider findUiProvider(String pid, Node context) {
-               CmsUiProvider found = null;
-               if (pid != null) {
-                       SortedMap<RankingKey, CmsUiProvider> subMap = uiProviders.subMap(RankingKey.minPid(pid),
-                                       RankingKey.maxPid(pid));
-                       providers: for (RankingKey key : subMap.keySet()) {
-                               if (key.getPid() == null || !key.getPid().equals(pid))
-                                       break providers;
-                               found = subMap.get(key);
-                       }
-                       if (found != null)
-                               return found;
-               }
-
-               if (found == null && context != null) {
-                       SortedMap<RankingKey, CmsUiProvider> subMap = null;
-                       String dataType = null;
-                       if (Jcr.isNodeType(context, EntityTypes.ENTITY_ENTITY)) {
-                               dataType = Jcr.get(context, EntityNames.ENTITY_TYPE);
-                               subMap = uiProviders.subMap(RankingKey.minDataType(dataType), RankingKey.maxDataType(dataType));
-                       }
-                       providers: for (RankingKey key : subMap.keySet()) {
-                               if (key.getDataType() == null || !key.getDataType().equals(dataType))
-                                       break providers;
-                               found = subMap.get(key);
-                       }
-                       if (found == null)
-                               found = uiProviders.get(new RankingKey(null, null, null, dataType, null));
-                       if (found != null)
-                               return found;
-               }
-
-               // nothing
-               if (log.isWarnEnabled())
-                       log.warn("No UI provider found for" + (pid != null ? " pid " + pid : "")
-                                       + (context != null ? " " + context : ""));
-               return new CmsUiProvider() {
-
-                       @Override
-                       public Control createUi(Composite parent, Node context) throws RepositoryException {
-                               return parent;
-                       }
-               };
-       }
-
-       @Override
-       public void setState(Composite parent, String state) {
-               CmsView cmsView = CmsView.getCmsView(parent);
-               // for the time being we systematically open a session, in order to make sure
-               // that home is initialised
-               Session session = null;
-               try {
-                       if (state != null && state.startsWith("/")) {
-                               String path = state.substring(1);
-                               String workspace;
-                               if (path.equals("")) {
-                                       workspace = null;
-                                       path = "/";
-                               } else {
-                                       int index = path.indexOf('/');
-                                       if (index == 0) {
-                                               log.error("Cannot interpret // " + state);
-                                               cmsView.navigateTo("~");
-                                               return;
-                                       } else if (index > 0) {
-                                               workspace = path.substring(0, index);
-                                               path = path.substring(index);
-                                       } else {// index<0, assuming root node
-                                               workspace = path;
-                                               path = "/";
-                                       }
-                               }
-                               session = getRepository().login(workspace);
-
-                               Node node = session.getNode(path);
-
-                               refreshEntityUi(null, node);
-                       }
-               } catch (RepositoryException e) {
-                       log.error("Cannot load state " + state, e);
-                       cmsView.navigateTo("~");
-               } finally {
-                       JcrUtils.logoutQuietly(session);
-               }
-       }
-
-       private void refreshEntityUi(Composite parent, Node context) {
-       }
-
-       /*
-        * Dependency injection.
-        */
-
-       public void addUiProvider(CmsUiProvider uiProvider, Map<String, Object> properties) {
-               RankingKey partKey = new RankingKey(properties);
-//             String servicePid = properties.get(Constants.SERVICE_PID);
-//             if (servicePid == null) {
-//                     log.error("No service pid found for " + uiProvider.getClass() + ", " + properties);
-//             } else {
-               if (partKey.getPid() != null || partKey.getDataType() != null) {
-                       uiProviders.put(partKey, uiProvider);
-                       if (log.isDebugEnabled())
-                               log.debug("Added UI provider " + partKey + " (" + uiProvider.getClass().getName() + ") to CMS app.");
-               }
-//             }
-
-       }
-
-       public void removeUiProvider(CmsUiProvider uiProvider, Map<String, Object> properties) {
-               RankingKey partKey = new RankingKey(properties);
-//             String servicePid = properties.get(Constants.SERVICE_PID);
-               uiProviders.remove(partKey);
-
-       }
-
-       @Override
-       public void handleEvent(Event event) {
-
-               // Specific UI related events
-               ArgeoSuiteUi ui = getRelatedUi(event);
-               if (isTopic(event, SuiteEvent.refreshPart)) {
-                       Node node = Jcr.getNodeById(ui.getSession(), get(event, SuiteEvent.NODE_ID));
-                       ui.getTabbedArea().view(findUiProvider(DASHBOARD_PID, node), node);
-//                     ui.layout(true, true);
-               } else if (isTopic(event, SuiteEvent.openNewPart)) {
-                       Node node = Jcr.getNodeById(ui.getSession(), get(event, SuiteEvent.NODE_ID));
-                       ui.getTabbedArea().open(findUiProvider(DASHBOARD_PID, node), node);
-//                     ui.layout(true, true);
-               } else if (isTopic(event, SuiteEvent.switchLayer)) {
-                       String layer = get(event, SuiteEvent.LAYER);
-                       ui.switchToLayer(layer);
-               }
-
-       }
-
-       private ArgeoSuiteUi getRelatedUi(Event event) {
-               return managedUis.get(get(event, CMS_VIEW_UID_PROPERTY));
-       }
-
-       private static boolean isTopic(Event event, CmsEvent cmsEvent) {
-               return event.getTopic().equals(cmsEvent.topic());
-       }
-
-       private static String get(Event event, String key) {
-               Object value = event.getProperty(key);
-               if (value == null)
-                       throw new IllegalArgumentException("Property " + key + " must be set");
-               return value.toString();
-
-       }
-}
diff --git a/org.argeo.suite.ui/src/org/argeo/suite/ui/ArgeoSuiteUi.java b/org.argeo.suite.ui/src/org/argeo/suite/ui/ArgeoSuiteUi.java
deleted file mode 100644 (file)
index 72210ad..0000000
+++ /dev/null
@@ -1,131 +0,0 @@
-package org.argeo.suite.ui;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import javax.jcr.Session;
-
-import org.argeo.cms.ui.CmsView;
-import org.argeo.cms.ui.util.CmsUiUtils;
-import org.argeo.cms.ui.widgets.TabbedArea;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.layout.FormLayout;
-import org.eclipse.swt.widgets.Composite;
-
-/** The {@link CmsView} for the work ergonomics of Argeo Suite. */
-public class ArgeoSuiteUi extends Composite {
-       private static final long serialVersionUID = 6207018859086689108L;
-
-       public final static String DASHBOARD_LAYER = "dashboard";
-       private Composite header;
-       private Composite belowHeader;
-       private Composite leadPane;
-       private Composite dynamicArea;
-
-       private Session session;
-
-       private Map<String, WorkLayer> layers = new HashMap<>();
-       private String currentLayer = DASHBOARD_LAYER;
-
-       public ArgeoSuiteUi(Composite parent, int style) {
-               super(parent, style);
-               this.setLayout(CmsUiUtils.noSpaceGridLayout());
-
-               header = new Composite(this, SWT.NONE);
-               CmsUiUtils.style(header, SuiteStyle.header);
-               header.setLayoutData(CmsUiUtils.fillWidth());
-
-               belowHeader = new Composite(this, SWT.NONE);
-               belowHeader.setLayoutData(CmsUiUtils.fillAll());
-       }
-
-       public void refreshBelowHeader(boolean initApp) {
-               CmsUiUtils.clear(belowHeader);
-               int style = getStyle();
-               if (initApp) {
-                       belowHeader.setLayout(CmsUiUtils.noSpaceGridLayout(2));
-
-                       if (SWT.RIGHT_TO_LEFT == (style & SWT.RIGHT_TO_LEFT)) {// arabic, hebrew, etc.
-                               dynamicArea = new Composite(belowHeader, SWT.NONE);
-                               leadPane = new Composite(belowHeader, SWT.NONE);
-                       } else {
-                               leadPane = new Composite(belowHeader, SWT.NONE);
-                               dynamicArea = new Composite(belowHeader, SWT.NONE);
-                       }
-                       leadPane.setLayoutData(CmsUiUtils.fillHeight());
-                       CmsUiUtils.style(leadPane, SuiteStyle.leadPane);
-                       dynamicArea.setLayoutData(CmsUiUtils.fillAll());
-
-                       dynamicArea.setLayout(new FormLayout());
-
-               } else {
-                       belowHeader.setLayout(CmsUiUtils.noSpaceGridLayout());
-               }
-       }
-
-       /*
-        * LAYERS
-        */
-
-       Composite getCurrentLayer() {
-               if (currentLayer == null)
-                       throw new IllegalStateException("No current layer");
-               return layers.get(currentLayer).getArea();
-       }
-
-       Composite getLayer(String id) {
-               if (!layers.containsKey(id))
-                       throw new IllegalArgumentException("No layer " + id + " is available.");
-               return layers.get(id).getArea();
-       }
-
-       Composite switchToLayer(String layer) {
-               Composite current = getCurrentLayer();
-               if (currentLayer.equals(layer))
-                       return current;
-               Composite toShow = getLayer(layer);
-               getDisplay().syncExec(() -> toShow.moveAbove(current));
-               currentLayer = layer;
-               return toShow;
-       }
-
-       void addLayer(String layer) {
-               WorkLayer workLayer = new WorkLayer(dynamicArea, getStyle());
-               layers.put(layer, workLayer);
-       }
-
-       /*
-        * GETTERS / SETTERS
-        */
-
-       Composite getHeader() {
-               return header;
-       }
-
-       Composite getLeadPane() {
-               return leadPane;
-       }
-
-       Composite getBelowHeader() {
-               return belowHeader;
-       }
-
-       Composite getEntryArea() {
-               return layers.get(currentLayer).getEntryArea();
-       }
-
-       TabbedArea getTabbedArea() {
-               return layers.get(currentLayer).getTabbedArea();
-       }
-
-       Session getSession() {
-               return session;
-       }
-
-       void setSession(Session session) {
-               this.session = session;
-       }
-
-       
-
-}
diff --git a/org.argeo.suite.ui/src/org/argeo/suite/ui/DefaultEditionLayer.java b/org.argeo.suite.ui/src/org/argeo/suite/ui/DefaultEditionLayer.java
new file mode 100644 (file)
index 0000000..9ed12b6
--- /dev/null
@@ -0,0 +1,93 @@
+package org.argeo.suite.ui;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.argeo.cms.ui.CmsTheme;
+import org.argeo.cms.ui.CmsUiProvider;
+import org.argeo.cms.ui.util.CmsUiUtils;
+import org.argeo.cms.ui.widgets.TabbedArea;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.SashForm;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+/** An app layer based on an entry area and an editor area. */
+public class DefaultEditionLayer implements SuiteLayer {
+       private CmsUiProvider entryArea;
+
+       @Override
+       public Control createUi(Composite parent, Node context) throws RepositoryException {
+               DefaultEditionArea workArea = new DefaultEditionArea(parent, parent.getStyle());
+               if (entryArea != null) {
+                       entryArea.createUi(workArea.getEntryArea(), context);
+               }
+               return workArea;
+       }
+
+       @Override
+       public void view(Composite workArea, Node context) {
+               TabbedArea tabbedArea = ((DefaultEditionArea) workArea).getTabbedArea();
+               CmsUiProvider uiProvider = null;
+               tabbedArea.view(uiProvider, context);
+       }
+
+       @Override
+       public void open(Composite workArea, Node context) {
+               TabbedArea tabbedArea = ((DefaultEditionArea) workArea).getTabbedArea();
+               CmsUiProvider uiProvider = null;
+               tabbedArea.open(uiProvider, context);
+       }
+
+       public void setEntryArea(CmsUiProvider entryArea) {
+               this.entryArea = entryArea;
+       }
+
+       class DefaultEditionArea extends SashForm {
+               private static final long serialVersionUID = 2219125778722702618L;
+               private CmsTheme theme;
+//             private SashForm area;
+               private Composite entryArea;
+               private Composite editorArea;
+               private TabbedArea tabbedArea;
+
+               DefaultEditionArea(Composite parent, int style) {
+                       super(parent, SWT.HORIZONTAL);
+                       theme = CmsTheme.getCmsTheme(parent);
+//                     area = new SashForm(parent, SWT.HORIZONTAL);
+//                     area.setLayoutData(CmsUiUtils.coversAll());
+
+                       if (SWT.RIGHT_TO_LEFT == (style & SWT.RIGHT_TO_LEFT)) {// arabic, hebrew, etc.
+                               editorArea = new Composite(this, SWT.BORDER);
+                               entryArea = new Composite(this, SWT.BORDER);
+                       } else {
+                               entryArea = new Composite(this, SWT.NONE);
+                               editorArea = new Composite(this, SWT.NONE);
+                       }
+                       int[] weights = new int[] { 2000, 8000 };
+                       setWeights(weights);
+//                     editorArea.setLayout(CmsUiUtils.noSpaceGridLayout());
+                       editorArea.setLayout(new GridLayout());
+
+                       tabbedArea = new TabbedArea(editorArea, SWT.NONE);
+                       tabbedArea.setBodyStyle(SuiteStyle.mainTabBody.toStyleClass());
+                       tabbedArea.setTabStyle(SuiteStyle.mainTab.toStyleClass());
+                       tabbedArea.setTabSelectedStyle(SuiteStyle.mainTabSelected.toStyleClass());
+                       tabbedArea.setCloseIcon(SuiteIcon.close.getSmallIcon(theme));
+                       tabbedArea.setLayoutData(CmsUiUtils.fillAll());
+               }
+
+//             Composite getArea() {
+//                     return area;
+//             }
+//
+               public Composite getEntryArea() {
+                       return entryArea;
+               }
+
+               public TabbedArea getTabbedArea() {
+                       return tabbedArea;
+               }
+       }
+}
\ No newline at end of file
index 1f371823aa1211c958d4ae27377b6041298cec5d..77ba555e3b1af3e636f8cb1e9c9ea014c28135aa 100644 (file)
@@ -5,6 +5,8 @@ import java.util.Map;
 import javax.jcr.Node;
 import javax.jcr.RepositoryException;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 import org.argeo.cms.Localized;
 import org.argeo.cms.ui.CmsTheme;
 import org.argeo.cms.ui.CmsUiProvider;
@@ -21,7 +23,13 @@ import org.eclipse.swt.widgets.Label;
 
 /** Side pane listing various perspectives. */
 public class DefaultLeadPane implements CmsUiProvider {
-       // private final static Log log = LogFactory.getLog(DefaultLeadPane.class);
+       private final static Log log = LogFactory.getLog(DefaultLeadPane.class);
+
+       public static enum Property {
+               defaultLayers;
+       }
+
+       private String[] defaultLayers;
 
        @Override
        public Control createUi(Composite parent, Node node) throws RepositoryException {
@@ -33,13 +41,20 @@ public class DefaultLeadPane implements CmsUiProvider {
                layout.marginRight = 10;
                parent.setLayout(layout);
 
-               Button dashboardB = createButton(parent, SuiteMsg.dashboard.name(), SuiteMsg.dashboard, SuiteIcon.dashboard);
+               Button first = null;
+               for (String layerId : defaultLayers) {
+                       Button b = createButton(parent, layerId, SuiteMsg.dashboard, SuiteIcon.dashboard);
+                       if (first == null)
+                               first = b;
+               }
+
+//             Button dashboardB = createButton(parent, SuiteMsg.dashboard.name(), SuiteMsg.dashboard, SuiteIcon.dashboard);
                if (!cmsView.isAnonymous()) {
 //                     createButton(parent, SuiteMsg.documents.name(), SuiteMsg.documents, SuiteIcon.documents);
 //                     createButton(parent, SuiteMsg.people.name(), SuiteMsg.people, SuiteIcon.people);
 //                     createButton(parent, SuiteMsg.locations.name(), SuiteMsg.locations, SuiteIcon.location);
                }
-               return dashboardB;
+               return first;
        }
 
        protected Button createButton(Composite parent, String layer, Localized msg, CmsIcon icon) {
@@ -57,7 +72,11 @@ public class DefaultLeadPane implements CmsUiProvider {
                return button;
        }
 
-       public void init(Map<String, String> properties) {
-
+       public void init(Map<String, Object> properties) {
+               defaultLayers = (String[]) properties.get(Property.defaultLayers.toString());
+               if (defaultLayers == null)
+                       throw new IllegalArgumentException("Default layers must be set.");
+               if (log.isDebugEnabled())
+                       log.debug("Default layers: " + defaultLayers);
        }
 }
diff --git a/org.argeo.suite.ui/src/org/argeo/suite/ui/SuiteApp.java b/org.argeo.suite.ui/src/org/argeo/suite/ui/SuiteApp.java
new file mode 100644 (file)
index 0000000..ce6f7f1
--- /dev/null
@@ -0,0 +1,348 @@
+package org.argeo.suite.ui;
+
+import static org.argeo.cms.ui.CmsView.CMS_VIEW_UID_PROPERTY;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.cms.ui.AbstractCmsApp;
+import org.argeo.cms.ui.CmsTheme;
+import org.argeo.cms.ui.CmsUiProvider;
+import org.argeo.cms.ui.CmsView;
+import org.argeo.cms.ui.dialogs.CmsFeedback;
+import org.argeo.cms.ui.util.CmsEvent;
+import org.argeo.cms.ui.util.CmsUiUtils;
+import org.argeo.jcr.Jcr;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.suite.RankedObject;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Composite;
+import org.osgi.framework.Constants;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventHandler;
+
+/** The Argeo Suite App. */
+public class SuiteApp extends AbstractCmsApp implements EventHandler {
+       private final static Log log = LogFactory.getLog(SuiteApp.class);
+
+       public final static String PID_PREFIX = "argeo.suite.ui.";
+       public final static String HEADER_PID = PID_PREFIX + "header";
+       public final static String LEAD_PANE_PID = PID_PREFIX + "leadPane";
+       public final static String LOGIN_SCREEN_PID = PID_PREFIX + "loginScreen";
+       public final static String DASHBOARD_LAYER_PID = PID_PREFIX + "dashboardLayer";
+       public final static String DASHBOARD_PID = PID_PREFIX + "dashboard";
+       public final static String RECENT_ITEMS_PID = PID_PREFIX + "recentItems";
+
+       private final static String DEFAULT_UI_NAME = "app";
+       private final static String DEFAULT_THEME_ID = "org.argeo.suite.theme.default";
+
+       private Map<String, RankedObject<CmsUiProvider>> uiProvidersByPid = Collections.synchronizedMap(new HashMap<>());
+       private Map<String, RankedObject<SuiteLayer>> layers = Collections.synchronizedSortedMap(new TreeMap<>());
+
+       // TODO make more optimal or via CmsSession/CmsView
+       private Map<String, SuiteUi> managedUis = new HashMap<>();
+
+//     private CmsUiProvider headerPart = null;
+
+       public void init(Map<String, String> properties) {
+               if (log.isDebugEnabled())
+                       log.info("Argeo Suite App started");
+       }
+
+       public void destroy(Map<String, String> properties) {
+               for (SuiteUi ui : managedUis.values())
+                       if (!ui.isDisposed())
+                               ui.dispose();
+               if (log.isDebugEnabled())
+                       log.info("Argeo Suite App stopped");
+
+       }
+
+       @Override
+       public Set<String> getUiNames() {
+               HashSet<String> uiNames = new HashSet<>();
+               uiNames.add(DEFAULT_UI_NAME);
+               return uiNames;
+       }
+
+       @Override
+       public Composite initUi(Composite parent) {
+               String uiName = parent.getData(UI_NAME_PROPERTY) != null ? parent.getData(UI_NAME_PROPERTY).toString() : null;
+               CmsView cmsView = CmsView.getCmsView(parent);
+               if (cmsView == null)
+                       throw new IllegalStateException("No CMS view is registered.");
+               CmsTheme theme = getTheme(uiName);
+               if (theme != null)
+                       CmsTheme.registerCmsTheme(parent.getShell(), theme);
+               SuiteUi argeoSuiteUi = new SuiteUi(parent, SWT.NONE);
+               String uid = cmsView.getUid();
+               managedUis.put(uid, argeoSuiteUi);
+               argeoSuiteUi.addDisposeListener((e) -> {
+                       managedUis.remove(uid);
+                       if (log.isDebugEnabled())
+                               log.debug("Suite UI " + uid + " has been disposed.");
+               });
+               refreshUi(argeoSuiteUi, null);
+               return argeoSuiteUi;
+       }
+
+       @Override
+       public String getThemeId(String uiName) {
+               // TODO make it configurable
+               return DEFAULT_THEME_ID;
+       }
+
+       @Override
+       public void refreshUi(Composite parent, String state) {
+               try {
+                       Node context = null;
+                       SuiteUi ui = (SuiteUi) parent;
+                       refreshPart(findUiProvider(HEADER_PID), ui.getHeader(), context);
+                       CmsView cmsView = CmsView.getCmsView(parent);
+                       if (cmsView.isAnonymous()) {
+                               ui.refreshBelowHeader(false);
+                               refreshPart(findUiProvider(LOGIN_SCREEN_PID), ui.getBelowHeader(), context);
+                       } else {
+                               try {
+                                       if (ui.getUserHome() == null)
+                                               ui.initSessions(getRepository());
+                                       context = ui.getUserHome();
+
+                               } catch (RepositoryException e) {
+                                       e.printStackTrace();
+                               }
+                               ui.refreshBelowHeader(true);
+
+                               for (String key : layers.keySet()) {
+                                       SuiteLayer layer = layers.get(key).get();
+                                       ui.addLayer(key, layer);
+                               }
+
+//                             ui.addLayer(ArgeoSuiteUi.DASHBOARD_LAYER);
+//                             ui.addLayer("documents");
+//                             ui.addLayer("locations");
+//                             ui.addLayer("people");
+                               ui.switchToLayer(DASHBOARD_LAYER_PID, context);
+
+//                             refreshPart(findUiProvider(DASHBOARD_PID), ui.getTabbedArea().getCurrent(), context);
+                               refreshPart(findUiProvider(LEAD_PANE_PID), ui.getLeadPane(), context);
+//                             refreshPart(findUiProvider(RECENT_ITEMS_PID), ui.getEntryArea(), context);
+                       }
+                       ui.layout(true, true);
+               } catch (Exception e) {
+                       CmsFeedback.show("Unexpected exception", e);
+               }
+       }
+
+       private void refreshPart(CmsUiProvider uiProvider, Composite part, Node context) {
+               CmsUiUtils.clear(part);
+               uiProvider.createUiPart(part, context);
+       }
+
+       private CmsUiProvider findUiProvider(String pid) {
+               if (!uiProvidersByPid.containsKey(pid))
+                       throw new IllegalArgumentException("No UI provider registered as " + pid);
+               return uiProvidersByPid.get(pid).get();
+       }
+//     private CmsUiProvider findUiProvider(String pid, Node context) {
+//             CmsUiProvider found = null;
+//             if (pid != null) {
+//                     SortedMap<RankingKey, CmsUiProvider> subMap = uiProvidersByPid.subMap(RankingKey.minPid(pid),
+//                                     RankingKey.maxPid(pid));
+//                     providers: for (RankingKey key : subMap.keySet()) {
+//                             if (key.getPid() == null || !key.getPid().equals(pid))
+//                                     break providers;
+//                             found = subMap.get(key);
+//                     }
+//                     if (found != null)
+//                             return found;
+//             }
+//
+//             if (found == null && context != null) {
+//                     SortedMap<RankingKey, CmsUiProvider> subMap = null;
+//                     String dataType = null;
+//                     if (Jcr.isNodeType(context, EntityTypes.ENTITY_ENTITY)) {
+//                             dataType = Jcr.get(context, EntityNames.ENTITY_TYPE);
+//                             subMap = uiProvidersByPid.subMap(RankingKey.minDataType(dataType), RankingKey.maxDataType(dataType));
+//                     }
+//                     providers: for (RankingKey key : subMap.keySet()) {
+//                             if (key.getDataType() == null || !key.getDataType().equals(dataType))
+//                                     break providers;
+//                             found = subMap.get(key);
+//                     }
+//                     if (found == null)
+//                             found = uiProvidersByPid.get(new RankingKey(null, null, null, dataType, null));
+//                     if (found != null)
+//                             return found;
+//             }
+//
+//             // nothing
+//             if (log.isWarnEnabled())
+//                     log.warn("No UI provider found for" + (pid != null ? " pid " + pid : "")
+//                                     + (context != null ? " " + context : ""));
+//             return new CmsUiProvider() {
+//
+//                     @Override
+//                     public Control createUi(Composite parent, Node context) throws RepositoryException {
+//                             return parent;
+//                     }
+//             };
+//     }
+
+       @Override
+       public void setState(Composite parent, String state) {
+               CmsView cmsView = CmsView.getCmsView(parent);
+               // for the time being we systematically open a session, in order to make sure
+               // that home is initialised
+               Session session = null;
+               try {
+                       if (state != null && state.startsWith("/")) {
+                               String path = state.substring(1);
+                               String workspace;
+                               if (path.equals("")) {
+                                       workspace = null;
+                                       path = "/";
+                               } else {
+                                       int index = path.indexOf('/');
+                                       if (index == 0) {
+                                               log.error("Cannot interpret // " + state);
+                                               cmsView.navigateTo("~");
+                                               return;
+                                       } else if (index > 0) {
+                                               workspace = path.substring(0, index);
+                                               path = path.substring(index);
+                                       } else {// index<0, assuming root node
+                                               workspace = path;
+                                               path = "/";
+                                       }
+                               }
+                               session = getRepository().login(workspace);
+
+                               Node node = session.getNode(path);
+
+                               refreshEntityUi(null, node);
+                       }
+               } catch (RepositoryException e) {
+                       log.error("Cannot load state " + state, e);
+                       cmsView.navigateTo("~");
+               } finally {
+                       JcrUtils.logoutQuietly(session);
+               }
+       }
+
+       private void refreshEntityUi(Composite parent, Node context) {
+       }
+
+       /*
+        * Dependency injection.
+        */
+
+       public void addUiProvider(CmsUiProvider uiProvider, Map<String, Object> properties) {
+//             RankingKey partKey = new RankingKey(properties);
+//             if (partKey.getPid() != null || partKey.getDataType() != null) {
+//                     uiProvidersByPid.put(partKey, uiProvider);
+//                     if (log.isDebugEnabled())
+//                             log.debug("Added UI provider " + partKey + " (" + uiProvider.getClass().getName() + ") to CMS app.");
+//             }
+
+               if (properties.containsKey(Constants.SERVICE_PID)) {
+                       String pid = (String) properties.get(Constants.SERVICE_PID);
+                       RankedObject.putIfHigherRank(uiProvidersByPid, pid, uiProvider, properties);
+//                     RankedObject<CmsUiProvider> rankedObject = new RankedObject<>(uiProvider, properties);
+//                     if (!uiProvidersByPid.containsKey(pid)) {
+//                             uiProvidersByPid.put(pid, rankedObject);
+//                             if (log.isDebugEnabled())
+//                                     log.debug("Added UI provider " + pid + " as " + uiProvider.getClass().getName() + " with rank "
+//                                                     + rankedObject.getRank());
+//                     } else {
+//                             RankedObject<CmsUiProvider> current = uiProvidersByPid.get(pid);
+//                             if (current.getRank() <= rankedObject.getRank()) {
+//                                     uiProvidersByPid.put(pid, rankedObject);
+//                                     if (log.isDebugEnabled())
+//                                             log.debug("Replaced UI provider " + pid + " by " + uiProvider.getClass().getName()
+//                                                             + " with rank " + rankedObject.getRank());
+//                             }
+//                     }
+               }
+       }
+
+       public void removeUiProvider(CmsUiProvider uiProvider, Map<String, Object> properties) {
+               if (properties.containsKey(Constants.SERVICE_PID)) {
+                       String pid = (String) properties.get(Constants.SERVICE_PID);
+                       if (uiProvidersByPid.containsKey(pid)) {
+                               if (uiProvidersByPid.get(pid).equals(new RankedObject<CmsUiProvider>(uiProvider, properties))) {
+                                       uiProvidersByPid.remove(pid);
+                               }
+                       }
+               }
+
+       }
+
+       public void addLayer(SuiteLayer layer, Map<String, Object> properties) {
+               if (properties.containsKey(Constants.SERVICE_PID)) {
+                       String pid = (String) properties.get(Constants.SERVICE_PID);
+                       RankedObject.putIfHigherRank(layers, pid, layer, properties);
+               }
+       }
+
+       public void removeLayer(SuiteLayer layer, Map<String, Object> properties) {
+               if (properties.containsKey(Constants.SERVICE_PID)) {
+                       String pid = (String) properties.get(Constants.SERVICE_PID);
+                       if (layers.containsKey(pid)) {
+                               if (layers.get(pid).equals(new RankedObject<SuiteLayer>(layer, properties))) {
+                                       layers.remove(pid);
+                               }
+                       }
+               }
+       }
+
+       @Override
+       public void handleEvent(Event event) {
+
+               // Specific UI related events
+               SuiteUi ui = getRelatedUi(event);
+               String currentLayerId = ui.getCurrentLayerId();
+               SuiteLayer layer = layers.get(currentLayerId).get();
+               if (isTopic(event, SuiteEvent.refreshPart)) {
+                       Node node = Jcr.getNodeById(ui.getSysSession(), get(event, SuiteEvent.NODE_ID));
+                       layer.view(ui.getCurrentWorkArea(), node);
+                       // ui.getTabbedArea().view(findUiProvider(DASHBOARD_PID), node);
+//                     ui.layout(true, true);
+               } else if (isTopic(event, SuiteEvent.openNewPart)) {
+                       Node node = Jcr.getNodeById(ui.getSysSession(), get(event, SuiteEvent.NODE_ID));
+                       layer.open(ui.getCurrentWorkArea(), node);
+//                     ui.getTabbedArea().open(findUiProvider(DASHBOARD_PID), node);
+//                     ui.layout(true, true);
+               } else if (isTopic(event, SuiteEvent.switchLayer)) {
+                       String layerId = get(event, SuiteEvent.LAYER);
+                       ui.switchToLayer(layerId, null);
+               }
+
+       }
+
+       private SuiteUi getRelatedUi(Event event) {
+               return managedUis.get(get(event, CMS_VIEW_UID_PROPERTY));
+       }
+
+       private static boolean isTopic(Event event, CmsEvent cmsEvent) {
+               return event.getTopic().equals(cmsEvent.topic());
+       }
+
+       private static String get(Event event, String key) {
+               Object value = event.getProperty(key);
+               if (value == null)
+                       throw new IllegalArgumentException("Property " + key + " must be set");
+               return value.toString();
+
+       }
+}
diff --git a/org.argeo.suite.ui/src/org/argeo/suite/ui/SuiteLayer.java b/org.argeo.suite.ui/src/org/argeo/suite/ui/SuiteLayer.java
new file mode 100644 (file)
index 0000000..6770c67
--- /dev/null
@@ -0,0 +1,15 @@
+package org.argeo.suite.ui;
+
+import javax.jcr.Node;
+
+import org.argeo.cms.ui.CmsUiProvider;
+import org.eclipse.swt.widgets.Composite;
+
+/** An UI layer for the main work area. */
+public interface SuiteLayer extends CmsUiProvider {
+       void view(Composite workArea, Node context);
+
+       default void open(Composite workArea, Node context) {
+               view(workArea, context);
+       }
+}
diff --git a/org.argeo.suite.ui/src/org/argeo/suite/ui/SuiteUi.java b/org.argeo.suite.ui/src/org/argeo/suite/ui/SuiteUi.java
new file mode 100644 (file)
index 0000000..c2c3c89
--- /dev/null
@@ -0,0 +1,175 @@
+package org.argeo.suite.ui;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.jcr.Node;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.argeo.api.NodeConstants;
+import org.argeo.api.NodeUtils;
+import org.argeo.cms.ui.CmsView;
+import org.argeo.cms.ui.util.CmsUiUtils;
+import org.argeo.jcr.Jcr;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.FormLayout;
+import org.eclipse.swt.widgets.Composite;
+
+/** The {@link CmsView} for the work ergonomics of Argeo Suite. */
+class SuiteUi extends Composite {
+       private static final long serialVersionUID = 6207018859086689108L;
+
+       private Composite header;
+       private Composite belowHeader;
+       private Composite leadPane;
+       private Composite dynamicArea;
+
+       private Session sysSession;
+       private Session homeSession;
+       private Node userHome;
+
+       private Map<String, SuiteLayer> layers = new HashMap<>();
+       private Map<String, Composite> workAreas = new HashMap<>();
+       private String currentLayerId = null;
+
+       private CmsView cmsView;
+
+       public SuiteUi(Composite parent, int style) {
+               super(parent, style);
+               cmsView = CmsView.getCmsView(parent);
+               this.setLayout(CmsUiUtils.noSpaceGridLayout());
+
+               header = new Composite(this, SWT.NONE);
+               CmsUiUtils.style(header, SuiteStyle.header);
+               header.setLayoutData(CmsUiUtils.fillWidth());
+
+               belowHeader = new Composite(this, SWT.NONE);
+               belowHeader.setLayoutData(CmsUiUtils.fillAll());
+       }
+
+       public void refreshBelowHeader(boolean initApp) {
+               CmsUiUtils.clear(belowHeader);
+               int style = getStyle();
+               if (initApp) {
+                       belowHeader.setLayout(CmsUiUtils.noSpaceGridLayout(2));
+
+                       if (SWT.RIGHT_TO_LEFT == (style & SWT.RIGHT_TO_LEFT)) {// arabic, hebrew, etc.
+                               dynamicArea = new Composite(belowHeader, SWT.NONE);
+                               leadPane = new Composite(belowHeader, SWT.NONE);
+                       } else {
+                               leadPane = new Composite(belowHeader, SWT.NONE);
+                               dynamicArea = new Composite(belowHeader, SWT.NONE);
+                       }
+                       leadPane.setLayoutData(CmsUiUtils.fillHeight());
+                       CmsUiUtils.style(leadPane, SuiteStyle.leadPane);
+                       dynamicArea.setLayoutData(CmsUiUtils.fillAll());
+
+                       dynamicArea.setLayout(new FormLayout());
+
+               } else {
+                       belowHeader.setLayout(CmsUiUtils.noSpaceGridLayout());
+               }
+       }
+
+       /*
+        * LAYERS
+        */
+
+       Composite getCurrentWorkArea() {
+               if (currentLayerId == null)
+                       throw new IllegalStateException("No current layer");
+               return workAreas.get(currentLayerId);
+       }
+
+       String getCurrentLayerId() {
+               return currentLayerId;
+       }
+
+       private Composite getLayer(String id, Node context) {
+               if (!layers.containsKey(id))
+                       throw new IllegalArgumentException("No layer " + id + " is available.");
+               if (!workAreas.containsKey(id))
+                       initLayer(id, layers.get(id), context);
+               return workAreas.get(id);
+       }
+
+       Composite switchToLayer(String layer, Node context) {
+               if (currentLayerId != null) {
+                       Composite current = getCurrentWorkArea();
+                       if (currentLayerId.equals(layer))
+                               return current;
+               }
+               if (context == null) {
+                       if (!cmsView.isAnonymous())
+                               context = userHome;
+               }
+               Composite toShow = getLayer(layer, context);
+               getDisplay().syncExec(() -> {
+                       toShow.moveAbove(null);
+                       dynamicArea.layout(true, true);
+               });
+               currentLayerId = layer;
+               return toShow;
+       }
+
+       void addLayer(String id, SuiteLayer layer) {
+               layers.put(id, layer);
+       }
+
+       void removeLayer(String id) {
+               layers.remove(id);
+               if (workAreas.containsKey(id)) {
+                       Composite workArea = workAreas.remove(id);
+                       if (!workArea.isDisposed())
+                               workArea.dispose();
+               }
+       }
+
+       protected Composite initLayer(String id, SuiteLayer layer, Node context) {
+               Composite workArea = cmsView.doAs(() -> (Composite) layer.createUiPart(dynamicArea, context));
+               workArea.setLayoutData(CmsUiUtils.coverAll());
+               workAreas.put(id, workArea);
+               return workArea;
+       }
+
+       /*
+        * GETTERS / SETTERS
+        */
+
+       Composite getHeader() {
+               return header;
+       }
+
+       Composite getLeadPane() {
+               return leadPane;
+       }
+
+       Composite getBelowHeader() {
+               return belowHeader;
+       }
+
+//     Session getSysSession() {
+//             return sysSession;
+//     }
+//
+       void initSessions(Repository repository) throws RepositoryException {
+               this.sysSession = repository.login();
+               this.homeSession = repository.login(NodeConstants.HOME_WORKSPACE);
+               userHome = NodeUtils.getUserHome(homeSession);
+               addDisposeListener((e) -> {
+                       Jcr.logout(sysSession);
+                       Jcr.logout(homeSession);
+               });
+       }
+
+       Node getUserHome() {
+               return userHome;
+       }
+
+       Session getSysSession() {
+               return sysSession;
+       }
+
+}
diff --git a/org.argeo.suite.ui/src/org/argeo/suite/ui/WorkLayer.java b/org.argeo.suite.ui/src/org/argeo/suite/ui/WorkLayer.java
deleted file mode 100644 (file)
index e2afb15..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-package org.argeo.suite.ui;
-
-import org.argeo.cms.ui.CmsTheme;
-import org.argeo.cms.ui.util.CmsUiUtils;
-import org.argeo.cms.ui.widgets.TabbedArea;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.custom.SashForm;
-import org.eclipse.swt.layout.GridLayout;
-import org.eclipse.swt.widgets.Composite;
-
-/** An app layer based on an entry area and an editor area. */
-public class WorkLayer {
-       private CmsTheme theme;
-       private SashForm area;
-       private Composite entryArea;
-       private Composite editorArea;
-       private TabbedArea tabbedArea;
-
-       WorkLayer(Composite parent, int style) {
-               theme = CmsTheme.getCmsTheme(parent);
-               area = new SashForm(parent, SWT.HORIZONTAL);
-               area.setLayoutData(CmsUiUtils.coversAll());
-
-               if (SWT.RIGHT_TO_LEFT == (style & SWT.RIGHT_TO_LEFT)) {// arabic, hebrew, etc.
-                       editorArea = new Composite(area, SWT.BORDER);
-                       entryArea = new Composite(area, SWT.BORDER);
-               } else {
-                       entryArea = new Composite(area, SWT.NONE);
-                       editorArea = new Composite(area, SWT.NONE);
-               }
-               int[] weights = new int[] { 2000, 8000 };
-               area.setWeights(weights);
-//                     editorArea.setLayout(CmsUiUtils.noSpaceGridLayout());
-               editorArea.setLayout(new GridLayout());
-
-               tabbedArea = new TabbedArea(editorArea, SWT.NONE);
-               tabbedArea.setBodyStyle(SuiteStyle.mainTabBody.toStyleClass());
-               tabbedArea.setTabStyle(SuiteStyle.mainTab.toStyleClass());
-               tabbedArea.setTabSelectedStyle(SuiteStyle.mainTabSelected.toStyleClass());
-               tabbedArea.setCloseIcon(SuiteIcon.close.getSmallIcon(theme));
-               tabbedArea.setLayoutData(CmsUiUtils.fillAll());
-       }
-
-       Composite getArea() {
-               return area;
-       }
-
-       Composite getEntryArea() {
-               return entryArea;
-       }
-
-       TabbedArea getTabbedArea() {
-               return tabbedArea;
-       }
-}
\ No newline at end of file
index ac30ba2b18cca64c24edb1d057c29cd348dfb933..ef7a063898caa57d162dbf702c5f21ff63a6950a 100644 (file)
@@ -15,6 +15,9 @@ org.argeo.suite.ui,\
 org.argeo.suite.theme.default,\
 org.argeo.suite.ui.rap
 
+argeo.osgi.start.6.suite=\
+org.argeo.documents.ui
+
 # Local
 argeo.node.repo.type=h2
 org.osgi.service.http.port=7070