From dec8c11591d7525c4be934406326823348daa461 Mon Sep 17 00:00:00 2001 From: Mathieu Baudier Date: Sat, 24 Oct 2020 13:05:11 +0200 Subject: [PATCH] Improve multi-layers and introduce documents layer. --- library/org.argeo.documents.ui/.project | 5 + .../OSGI-INF/documentsLayer.xml | 9 + .../OSGI-INF/entryArea.xml | 9 + library/org.argeo.documents.ui/bnd.bnd | 5 + .../org.argeo.documents.ui/build.properties | 6 +- .../config/documentsLayer.properties | 1 + .../config/entryArea.properties | 1 + .../documents/ui/DocumentsFileComposite.java | 3 +- .../documents/ui/DocumentsTreeUiProvider.java | 40 ++ .../argeo/documents/ui/DocumentsUtils.java | 88 ----- .../src/org/argeo/suite/RankedObject.java | 98 +++++ org.argeo.suite.ui/OSGI-INF/cmsApp.xml | 5 +- .../OSGI-INF/dashboardLayer.xml | 9 + org.argeo.suite.ui/OSGI-INF/leadPane.xml | 3 + org.argeo.suite.ui/bnd.bnd | 3 +- org.argeo.suite.ui/build.properties | 3 +- .../config/dashboardLayer.properties | 1 + .../src/org/argeo/suite/ui/ArgeoSuiteApp.java | 298 --------------- .../src/org/argeo/suite/ui/ArgeoSuiteUi.java | 131 ------- .../argeo/suite/ui/DefaultEditionLayer.java | 93 +++++ .../org/argeo/suite/ui/DefaultLeadPane.java | 29 +- .../src/org/argeo/suite/ui/SuiteApp.java | 348 ++++++++++++++++++ .../src/org/argeo/suite/ui/SuiteLayer.java | 15 + .../src/org/argeo/suite/ui/SuiteUi.java | 175 +++++++++ .../src/org/argeo/suite/ui/WorkLayer.java | 55 --- sdk/argeo-suite-rap.properties | 3 + 26 files changed, 852 insertions(+), 584 deletions(-) create mode 100644 library/org.argeo.documents.ui/OSGI-INF/documentsLayer.xml create mode 100644 library/org.argeo.documents.ui/OSGI-INF/entryArea.xml create mode 100644 library/org.argeo.documents.ui/config/documentsLayer.properties create mode 100644 library/org.argeo.documents.ui/config/entryArea.properties create mode 100644 library/org.argeo.documents.ui/src/org/argeo/documents/ui/DocumentsTreeUiProvider.java delete mode 100644 library/org.argeo.documents.ui/src/org/argeo/documents/ui/DocumentsUtils.java create mode 100644 org.argeo.suite.core/src/org/argeo/suite/RankedObject.java create mode 100644 org.argeo.suite.ui/OSGI-INF/dashboardLayer.xml create mode 100644 org.argeo.suite.ui/config/dashboardLayer.properties delete mode 100644 org.argeo.suite.ui/src/org/argeo/suite/ui/ArgeoSuiteApp.java delete mode 100644 org.argeo.suite.ui/src/org/argeo/suite/ui/ArgeoSuiteUi.java create mode 100644 org.argeo.suite.ui/src/org/argeo/suite/ui/DefaultEditionLayer.java create mode 100644 org.argeo.suite.ui/src/org/argeo/suite/ui/SuiteApp.java create mode 100644 org.argeo.suite.ui/src/org/argeo/suite/ui/SuiteLayer.java create mode 100644 org.argeo.suite.ui/src/org/argeo/suite/ui/SuiteUi.java delete mode 100644 org.argeo.suite.ui/src/org/argeo/suite/ui/WorkLayer.java diff --git a/library/org.argeo.documents.ui/.project b/library/org.argeo.documents.ui/.project index 27b103e..c046a7d 100644 --- a/library/org.argeo.documents.ui/.project +++ b/library/org.argeo.documents.ui/.project @@ -20,6 +20,11 @@ + + org.eclipse.pde.ds.core.builder + + + org.eclipse.pde.PluginNature 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 index 0000000..3833237 --- /dev/null +++ b/library/org.argeo.documents.ui/OSGI-INF/documentsLayer.xml @@ -0,0 +1,9 @@ + + + + + + + + + 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 index 0000000..eed1520 --- /dev/null +++ b/library/org.argeo.documents.ui/OSGI-INF/entryArea.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/library/org.argeo.documents.ui/bnd.bnd b/library/org.argeo.documents.ui/bnd.bnd index 4ce2057..cf46028 100644 --- a/library/org.argeo.documents.ui/bnd.bnd +++ b/library/org.argeo.documents.ui/bnd.bnd @@ -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 diff --git a/library/org.argeo.documents.ui/build.properties b/library/org.argeo.documents.ui/build.properties index 34d2e4d..6ac35f7 100644 --- a/library/org.argeo.documents.ui/build.properties +++ b/library/org.argeo.documents.ui/build.properties @@ -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 index 0000000..2906f2d --- /dev/null +++ b/library/org.argeo.documents.ui/config/documentsLayer.properties @@ -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 index 0000000..43b08f0 --- /dev/null +++ b/library/org.argeo.documents.ui/config/entryArea.properties @@ -0,0 +1 @@ +service.pid=argeo.documents.ui.entryArea diff --git a/library/org.argeo.documents.ui/src/org/argeo/documents/ui/DocumentsFileComposite.java b/library/org.argeo.documents.ui/src/org/argeo/documents/ui/DocumentsFileComposite.java index e0eaf1e..84c56a3 100644 --- a/library/org.argeo.documents.ui/src/org/argeo/documents/ui/DocumentsFileComposite.java +++ b/library/org.argeo.documents.ui/src/org/argeo/documents/ui/DocumentsFileComposite.java @@ -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 index 0000000..224576e --- /dev/null +++ b/library/org.argeo.documents.ui/src/org/argeo/documents/ui/DocumentsTreeUiProvider.java @@ -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 index ad103c6..0000000 --- a/library/org.argeo.documents.ui/src/org/argeo/documents/ui/DocumentsUtils.java +++ /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 index 0000000..a4d2833 --- /dev/null +++ b/org.argeo.suite.core/src/org/argeo/suite/RankedObject.java @@ -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 { + 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 properties; + private final Long rank; + + public RankedObject(T object, Map properties) { + this(object, properties, extractRanking(properties)); + } + + public RankedObject(T object, Map properties, Long rank) { + super(); + this.object = object; + this.properties = properties; + this.rank = rank; + } + + private static Long extractRanking(Map 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 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 RankedObject putIfHigherRank(Map> map, K key, T object, + Map properties) { + RankedObject 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 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; + } + } + + } + +} diff --git a/org.argeo.suite.ui/OSGI-INF/cmsApp.xml b/org.argeo.suite.ui/OSGI-INF/cmsApp.xml index 5a26f52..75584fc 100644 --- a/org.argeo.suite.ui/OSGI-INF/cmsApp.xml +++ b/org.argeo.suite.ui/OSGI-INF/cmsApp.xml @@ -1,6 +1,6 @@ - + @@ -8,5 +8,6 @@ - + + diff --git a/org.argeo.suite.ui/OSGI-INF/dashboardLayer.xml b/org.argeo.suite.ui/OSGI-INF/dashboardLayer.xml new file mode 100644 index 0000000..b60eafc --- /dev/null +++ b/org.argeo.suite.ui/OSGI-INF/dashboardLayer.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/org.argeo.suite.ui/OSGI-INF/leadPane.xml b/org.argeo.suite.ui/OSGI-INF/leadPane.xml index ebd348b..bcbc88b 100644 --- a/org.argeo.suite.ui/OSGI-INF/leadPane.xml +++ b/org.argeo.suite.ui/OSGI-INF/leadPane.xml @@ -5,4 +5,7 @@ + argeo.suite.ui.dashboardLayer +argeo.documents.ui.documentsLayer + diff --git a/org.argeo.suite.ui/bnd.bnd b/org.argeo.suite.ui/bnd.bnd index b8793ec..b5c8daa 100644 --- a/org.argeo.suite.ui/bnd.bnd +++ b/org.argeo.suite.ui/bnd.bnd @@ -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,\ diff --git a/org.argeo.suite.ui/build.properties b/org.argeo.suite.ui/build.properties index 1a1bc1a..d829967 100644 --- a/org.argeo.suite.ui/build.properties +++ b/org.argeo.suite.ui/build.properties @@ -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 index 0000000..c7b815b --- /dev/null +++ b/org.argeo.suite.ui/config/dashboardLayer.properties @@ -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 index db71387..0000000 --- a/org.argeo.suite.ui/src/org/argeo/suite/ui/ArgeoSuiteApp.java +++ /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 uiProviders = Collections.synchronizedSortedMap(new TreeMap<>()); - - // TODO make more optimal or via CmsSession/CmsView - private Map managedUis = new HashMap<>(); - -// private CmsUiProvider headerPart = null; - - public void init(Map properties) { - if (log.isDebugEnabled()) - log.info("Argeo Suite App started"); - } - - public void destroy(Map properties) { - for (ArgeoSuiteUi ui : managedUis.values()) - if (!ui.isDisposed()) - ui.dispose(); - if (log.isDebugEnabled()) - log.info("Argeo Suite App stopped"); - - } - - @Override - public Set getUiNames() { - HashSet 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 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 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 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 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 index 72210ad..0000000 --- a/org.argeo.suite.ui/src/org/argeo/suite/ui/ArgeoSuiteUi.java +++ /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 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 index 0000000..9ed12b6 --- /dev/null +++ b/org.argeo.suite.ui/src/org/argeo/suite/ui/DefaultEditionLayer.java @@ -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 diff --git a/org.argeo.suite.ui/src/org/argeo/suite/ui/DefaultLeadPane.java b/org.argeo.suite.ui/src/org/argeo/suite/ui/DefaultLeadPane.java index 1f37182..77ba555 100644 --- a/org.argeo.suite.ui/src/org/argeo/suite/ui/DefaultLeadPane.java +++ b/org.argeo.suite.ui/src/org/argeo/suite/ui/DefaultLeadPane.java @@ -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 properties) { - + public void init(Map 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 index 0000000..ce6f7f1 --- /dev/null +++ b/org.argeo.suite.ui/src/org/argeo/suite/ui/SuiteApp.java @@ -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> uiProvidersByPid = Collections.synchronizedMap(new HashMap<>()); + private Map> layers = Collections.synchronizedSortedMap(new TreeMap<>()); + + // TODO make more optimal or via CmsSession/CmsView + private Map managedUis = new HashMap<>(); + +// private CmsUiProvider headerPart = null; + + public void init(Map properties) { + if (log.isDebugEnabled()) + log.info("Argeo Suite App started"); + } + + public void destroy(Map properties) { + for (SuiteUi ui : managedUis.values()) + if (!ui.isDisposed()) + ui.dispose(); + if (log.isDebugEnabled()) + log.info("Argeo Suite App stopped"); + + } + + @Override + public Set getUiNames() { + HashSet 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 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 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 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 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 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 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(uiProvider, properties))) { + uiProvidersByPid.remove(pid); + } + } + } + + } + + public void addLayer(SuiteLayer layer, Map 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 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(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 index 0000000..6770c67 --- /dev/null +++ b/org.argeo.suite.ui/src/org/argeo/suite/ui/SuiteLayer.java @@ -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 index 0000000..c2c3c89 --- /dev/null +++ b/org.argeo.suite.ui/src/org/argeo/suite/ui/SuiteUi.java @@ -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 layers = new HashMap<>(); + private Map 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 index e2afb15..0000000 --- a/org.argeo.suite.ui/src/org/argeo/suite/ui/WorkLayer.java +++ /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 diff --git a/sdk/argeo-suite-rap.properties b/sdk/argeo-suite-rap.properties index ac30ba2..ef7a063 100644 --- a/sdk/argeo-suite-rap.properties +++ b/sdk/argeo-suite-rap.properties @@ -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 -- 2.30.2