Improve Eclipse 4 edition lifecycle
authorMathieu Baudier <mbaudier@argeo.org>
Mon, 26 Mar 2018 12:20:53 +0000 (14:20 +0200)
committerMathieu Baudier <mbaudier@argeo.org>
Mon, 26 Mar 2018 12:20:53 +0000 (14:20 +0200)
org.argeo.suite.e4/META-INF/MANIFEST.MF
org.argeo.suite.e4/e4xmi/argeo-office.e4xmi
org.argeo.suite.e4/src/org/argeo/suite/e4/parts/AbstractSuiteDashboard.java [new file with mode: 0644]
org.argeo.suite.e4/src/org/argeo/suite/e4/parts/DefaultDashboardEditor.java [new file with mode: 0644]

index b5f2931b96bec0151b96f7458b329207f944ec71..3531127c97bf041b621b0da40212996cee37e4ae 100644 (file)
@@ -12,6 +12,8 @@ Import-Package: javax.inject;version="1.0.0",
  org.apache.commons.logging;version="1.1.1",\r
  org.argeo.activities;version="2.1.79.SNAPSHOT-r201803241506",\r
  org.argeo.activities.ui;version="2.1.79.SNAPSHOT-r201803241506",\r
  org.apache.commons.logging;version="1.1.1",\r
  org.argeo.activities;version="2.1.79.SNAPSHOT-r201803241506",\r
  org.argeo.activities.ui;version="2.1.79.SNAPSHOT-r201803241506",\r
+ org.argeo.cms.auth;version="2.1.73.SNAPSHOT-r201803221729",\r
+ org.argeo.cms.ui.eclipse.forms;version="2.1.73.SNAPSHOT-r201803221729",\r
  org.argeo.cms.util;version="2.1.73.SNAPSHOT-r201803221729",\r
  org.argeo.connect;version="2.1.79.SNAPSHOT-r201803241506",\r
  org.argeo.connect.e4.handlers,\r
  org.argeo.cms.util;version="2.1.73.SNAPSHOT-r201803221729",\r
  org.argeo.connect;version="2.1.79.SNAPSHOT-r201803241506",\r
  org.argeo.connect.e4.handlers,\r
@@ -22,12 +24,17 @@ Import-Package: javax.inject;version="1.0.0",
  org.argeo.connect.util;version="2.1.79.SNAPSHOT-r201803241506",\r
  org.argeo.eclipse.ui;version="2.1.73.SNAPSHOT-r201803221729",\r
  org.argeo.jcr;version="2.1.73.SNAPSHOT-r201803221729",\r
  org.argeo.connect.util;version="2.1.79.SNAPSHOT-r201803241506",\r
  org.argeo.eclipse.ui;version="2.1.73.SNAPSHOT-r201803221729",\r
  org.argeo.jcr;version="2.1.73.SNAPSHOT-r201803221729",\r
+ org.argeo.node;version="2.1.0",\r
  org.argeo.people;version="2.1.79.SNAPSHOT-r201803241506",\r
  org.argeo.people.e4,\r
  org.argeo.people.e4.parts,\r
  org.argeo.people.ui.providers;version="2.1.79.SNAPSHOT-r201803241506",\r
  org.argeo.tracker;version="2.1.79.SNAPSHOT-r201803241506",\r
  org.argeo.people;version="2.1.79.SNAPSHOT-r201803241506",\r
  org.argeo.people.e4,\r
  org.argeo.people.e4.parts,\r
  org.argeo.people.ui.providers;version="2.1.79.SNAPSHOT-r201803241506",\r
  org.argeo.tracker;version="2.1.79.SNAPSHOT-r201803241506",\r
+ org.argeo.tracker.core;version="2.1.79.SNAPSHOT-r201803241506",\r
  org.argeo.tracker.ui;version="2.1.79.SNAPSHOT-r201803241506",\r
  org.argeo.tracker.ui;version="2.1.79.SNAPSHOT-r201803241506",\r
+ org.eclipse.core.commands.common,\r
+ org.eclipse.core.runtime;version="3.5.0",\r
+ org.eclipse.e4.ui.di,\r
  org.eclipse.jface,\r
  org.eclipse.jface.layout,\r
  org.eclipse.jface.viewers,\r
  org.eclipse.jface,\r
  org.eclipse.jface.layout,\r
  org.eclipse.jface.viewers,\r
index 263881f37281d2c4715a907e60889356833d971b..f7fd8c9d838f0eeab7a0c576344e4b6c973a6e8f 100644 (file)
@@ -6,17 +6,23 @@
     <children xsi:type="advanced:PerspectiveStack" xmi:id="_gm_toDAVEei3AbO1ldMI0w" elementId="org.argeo.suite.e4.perspectivestack.0">
       <children xsi:type="advanced:Perspective" xmi:id="_hr-YsDAVEei3AbO1ldMI0w" elementId="org.argeo.suite.e4.perspective.dashboard" label="Dashboard">
         <children xsi:type="basic:PartSashContainer" xmi:id="_mAC18DAVEei3AbO1ldMI0w" elementId="org.argeo.suite.e4.partsashcontainer.0" horizontal="true">
     <children xsi:type="advanced:PerspectiveStack" xmi:id="_gm_toDAVEei3AbO1ldMI0w" elementId="org.argeo.suite.e4.perspectivestack.0">
       <children xsi:type="advanced:Perspective" xmi:id="_hr-YsDAVEei3AbO1ldMI0w" elementId="org.argeo.suite.e4.perspective.dashboard" label="Dashboard">
         <children xsi:type="basic:PartSashContainer" xmi:id="_mAC18DAVEei3AbO1ldMI0w" elementId="org.argeo.suite.e4.partsashcontainer.0" horizontal="true">
-          <children xsi:type="basic:PartStack" xmi:id="_orJxUDAVEei3AbO1ldMI0w" elementId="org.argeo.suite.e4.partstack.0" containerData="4000">
-            <children xsi:type="basic:Part" xmi:id="_qAM4sDAVEei3AbO1ldMI0w" elementId="org.argeo.suite.e4.part.search" containerData="" contributionURI="bundleclass://org.argeo.suite.e4/org.argeo.suite.e4.parts.QuickSearchView" label="Search"/>
+          <children xsi:type="basic:PartStack" xmi:id="_orJxUDAVEei3AbO1ldMI0w" elementId="org.argeo.suite.e4.partstack.0" containerData="3000">
+            <tags>minimized</tags>
+            <children xsi:type="basic:Part" xmi:id="_qAM4sDAVEei3AbO1ldMI0w" elementId="org.argeo.suite.e4.part.search" containerData="" contributionURI="bundleclass://org.argeo.suite.e4/org.argeo.suite.e4.parts.QuickSearchView" label="Search" iconURI="platform:/plugin/org.argeo.theme.argeo2/icons/actions/search.png"/>
           </children>
           </children>
-          <children xsi:type="basic:PartStack" xmi:id="_vLza4DAVEei3AbO1ldMI0w" elementId="org.argeo.suite.e4.partstack.1" containerData="6000">
+          <children xsi:type="basic:PartStack" xmi:id="_vLza4DAVEei3AbO1ldMI0w" elementId="org.argeo.suite.e4.partstack.1" containerData="7000">
             <tags>entityEditorArea</tags>
             <tags>entityEditorArea</tags>
+            <children xsi:type="basic:Part" xmi:id="_n83mkDDnEeiUfOa4rxEKwQ" elementId="org.argeo.suite.e4.part.0" contributionURI="bundleclass://org.argeo.suite.e4/org.argeo.suite.e4.parts.DefaultDashboardEditor" iconURI="platform:/plugin/org.argeo.theme.argeo2/icons/types/dashboard.png"/>
           </children>
         </children>
       </children>
     </children>
     <trimBars xmi:id="_24OSADArEei3AbO1ldMI0w" elementId="org.argeo.suite.e4.trimbar.0">
           </children>
         </children>
       </children>
     </children>
     <trimBars xmi:id="_24OSADArEei3AbO1ldMI0w" elementId="org.argeo.suite.e4.trimbar.0">
-      <children xsi:type="menu:ToolBar" xmi:id="_3e5QcDArEei3AbO1ldMI0w" elementId="org.argeo.suite.e4.toolbar.0">
+      <children xsi:type="menu:ToolBar" xmi:id="_JIUjADDhEeiUfOa4rxEKwQ" elementId="org.argeo.suite.e4.toolbar.standard">
+        <children xsi:type="menu:HandledToolItem" xmi:id="_P-4LIDDhEeiUfOa4rxEKwQ" elementId="org.argeo.suite.e4.handledtoolitem.save" label="Save" iconURI="platform:/plugin/org.argeo.theme.argeo2/icons/actions/save.png" enabled="false" command="_1lO94DDgEeiUfOa4rxEKwQ"/>
+        <children xsi:type="menu:HandledToolItem" xmi:id="_W4V9UDDhEeiUfOa4rxEKwQ" elementId="org.argeo.suite.e4.handledtoolitem.saveall" label="Save All" iconURI="platform:/plugin/org.argeo.theme.argeo2/icons/actions/save-all.png" enabled="false" command="_5EpEADDgEeiUfOa4rxEKwQ"/>
+        <children xsi:type="menu:HandledToolItem" xmi:id="_bV4tYDDmEeiUfOa4rxEKwQ" elementId="org.argeo.suite.e4.handledtoolitem.closeall" label="Close All" iconURI="platform:/plugin/org.argeo.theme.argeo2/icons/actions/close-all.png" command="_VdldQDDmEeiUfOa4rxEKwQ"/>
+        <children xsi:type="menu:ToolBarSeparator" xmi:id="_Bn7o0DDjEeiUfOa4rxEKwQ" elementId="org.argeo.suite.e4.toolbarseparator.0"/>
         <children xsi:type="menu:HandledToolItem" xmi:id="_4Ssr8DArEei3AbO1ldMI0w" elementId="org.argeo.suite.e4.handledtoolitem.createTask" label="New Task" iconURI="platform:/plugin/org.argeo.theme.argeo2/icons/types/task.png" command="_2Xkt8DApEei3AbO1ldMI0w">
           <parameters xmi:id="_NYpK8DAsEei3AbO1ldMI0w" elementId="targetNodeType" name="targetNodeType" value="activities:task"/>
         </children>
         <children xsi:type="menu:HandledToolItem" xmi:id="_4Ssr8DArEei3AbO1ldMI0w" elementId="org.argeo.suite.e4.handledtoolitem.createTask" label="New Task" iconURI="platform:/plugin/org.argeo.theme.argeo2/icons/types/task.png" command="_2Xkt8DApEei3AbO1ldMI0w">
           <parameters xmi:id="_NYpK8DAsEei3AbO1ldMI0w" elementId="targetNodeType" name="targetNodeType" value="activities:task"/>
         </children>
     </trimBars>
   </children>
   <handlers xmi:id="_ipB00DArEei3AbO1ldMI0w" elementId="org.argeo.suite.e4.handler.0" contributionURI="bundleclass://org.argeo.connect.e4/org.argeo.connect.e4.handlers.CreateEntity" command="_2Xkt8DApEei3AbO1ldMI0w"/>
     </trimBars>
   </children>
   <handlers xmi:id="_ipB00DArEei3AbO1ldMI0w" elementId="org.argeo.suite.e4.handler.0" contributionURI="bundleclass://org.argeo.connect.e4/org.argeo.connect.e4.handlers.CreateEntity" command="_2Xkt8DApEei3AbO1ldMI0w"/>
-  <descriptors xmi:id="_pud7kDAtEei3AbO1ldMI0w" elementId="org.argeo.suite.e4.partdescriptor.taskEditor" label="Task" allowMultiple="true" category="entityEditorArea" closeable="true" dirtyable="true" contributionURI="bundleclass://org.argeo.connect.e4/org.argeo.activities.e4.parts.TaskEditor"/>
-  <descriptors xmi:id="_JsNs8DBiEei3AbO1ldMI0w" elementId="org.argeo.suite.e4.partdescriptor.personEditor" label="Person" allowMultiple="true" category="entityEditorArea" closeable="true" dirtyable="true" contributionURI="bundleclass://org.argeo.connect.e4/org.argeo.people.e4.parts.PersonEditor"/>
-  <descriptors xmi:id="_RxqxIDBiEei3AbO1ldMI0w" elementId="org.argeo.suite.e4.partdescriptor.orgEditor" label="Organisation" allowMultiple="true" category="entityEditorArea" closeable="true" dirtyable="true" contributionURI="bundleclass://org.argeo.connect.e4/org.argeo.people.e4.parts.OrgEditor"/>
+  <handlers xmi:id="_9gu00DDgEeiUfOa4rxEKwQ" elementId="org.argeo.suite.e4.handler.1" contributionURI="bundleclass://org.argeo.connect.e4/org.argeo.connect.e4.handlers.SavePart" command="_1lO94DDgEeiUfOa4rxEKwQ"/>
+  <handlers xmi:id="_ADDAkDDhEeiUfOa4rxEKwQ" elementId="org.argeo.suite.e4.handler.2" contributionURI="bundleclass://org.argeo.connect.e4/org.argeo.connect.e4.handlers.SaveAllParts" command="_5EpEADDgEeiUfOa4rxEKwQ"/>
+  <handlers xmi:id="_ZBWe8DDmEeiUfOa4rxEKwQ" elementId="org.argeo.suite.e4.handler.3" contributionURI="bundleclass://org.argeo.connect.e4/org.argeo.connect.e4.handlers.CloseAllParts" command="_VdldQDDmEeiUfOa4rxEKwQ"/>
+  <bindingTables xmi:id="_gxEm4DDqEeiUfOa4rxEKwQ" elementId="org.argeo.suite.e4.bindingtable.0">
+    <bindings xmi:id="_hj9cYDDqEeiUfOa4rxEKwQ" elementId="org.argeo.suite.e4.keybinding.0" keySequence="CTRL+S" command="_1lO94DDgEeiUfOa4rxEKwQ"/>
+    <bindings xmi:id="_pEAGYDDqEeiUfOa4rxEKwQ" elementId="org.argeo.suite.e4.keybinding.1" keySequence="SHIFT+CTRL+S" command="_5EpEADDgEeiUfOa4rxEKwQ"/>
+  </bindingTables>
+  <descriptors xmi:id="_pud7kDAtEei3AbO1ldMI0w" elementId="org.argeo.suite.e4.partdescriptor.taskEditor" label="Task" iconURI="platform:/plugin/org.argeo.theme.argeo2/icons/types/task.png" allowMultiple="true" category="entityEditorArea" closeable="true" dirtyable="true" contributionURI="bundleclass://org.argeo.connect.e4/org.argeo.activities.e4.parts.TaskEditor"/>
+  <descriptors xmi:id="_JsNs8DBiEei3AbO1ldMI0w" elementId="org.argeo.suite.e4.partdescriptor.personEditor" label="Person" iconURI="platform:/plugin/org.argeo.theme.argeo2/icons/types/person.png" allowMultiple="true" category="entityEditorArea" closeable="true" dirtyable="true" contributionURI="bundleclass://org.argeo.connect.e4/org.argeo.people.e4.parts.PersonEditor"/>
+  <descriptors xmi:id="_RxqxIDBiEei3AbO1ldMI0w" elementId="org.argeo.suite.e4.partdescriptor.orgEditor" label="Organisation" iconURI="platform:/plugin/org.argeo.theme.argeo2/icons/types/organisation.png" allowMultiple="true" category="entityEditorArea" closeable="true" dirtyable="true" contributionURI="bundleclass://org.argeo.connect.e4/org.argeo.people.e4.parts.OrgEditor"/>
   <commands xmi:id="_2Xkt8DApEei3AbO1ldMI0w" elementId="org.argeo.suite.e4.command.createEntity" commandName="Create">
     <parameters xmi:id="_GnXrcDAsEei3AbO1ldMI0w" elementId="targetNodeType" name="targetNodeType" optional="false"/>
   </commands>
   <commands xmi:id="_2Xkt8DApEei3AbO1ldMI0w" elementId="org.argeo.suite.e4.command.createEntity" commandName="Create">
     <parameters xmi:id="_GnXrcDAsEei3AbO1ldMI0w" elementId="targetNodeType" name="targetNodeType" optional="false"/>
   </commands>
+  <commands xmi:id="_1lO94DDgEeiUfOa4rxEKwQ" elementId="org.argeo.suite.e4.command.savePart" commandName="Save"/>
+  <commands xmi:id="_5EpEADDgEeiUfOa4rxEKwQ" elementId="org.argeo.suite.e4.command.saveAll" commandName="Save All"/>
+  <commands xmi:id="_VdldQDDmEeiUfOa4rxEKwQ" elementId="org.argeo.suite.e4.command.closeAll" commandName="Close All"/>
   <addons xmi:id="_bQjNYTAVEei3AbO1ldMI0w" elementId="org.eclipse.e4.core.commands.service" contributionURI="bundleclass://org.eclipse.e4.core.commands/org.eclipse.e4.core.commands.CommandServiceAddon"/>
   <addons xmi:id="_bQjNYjAVEei3AbO1ldMI0w" elementId="org.eclipse.e4.ui.contexts.service" contributionURI="bundleclass://org.eclipse.e4.ui.services/org.eclipse.e4.ui.services.ContextServiceAddon"/>
   <addons xmi:id="_bQjNYzAVEei3AbO1ldMI0w" elementId="org.eclipse.e4.ui.bindings.service" contributionURI="bundleclass://org.eclipse.e4.ui.bindings/org.eclipse.e4.ui.bindings.BindingServiceAddon"/>
   <addons xmi:id="_bQjNYTAVEei3AbO1ldMI0w" elementId="org.eclipse.e4.core.commands.service" contributionURI="bundleclass://org.eclipse.e4.core.commands/org.eclipse.e4.core.commands.CommandServiceAddon"/>
   <addons xmi:id="_bQjNYjAVEei3AbO1ldMI0w" elementId="org.eclipse.e4.ui.contexts.service" contributionURI="bundleclass://org.eclipse.e4.ui.services/org.eclipse.e4.ui.services.ContextServiceAddon"/>
   <addons xmi:id="_bQjNYzAVEei3AbO1ldMI0w" elementId="org.eclipse.e4.ui.bindings.service" contributionURI="bundleclass://org.eclipse.e4.ui.bindings/org.eclipse.e4.ui.bindings.BindingServiceAddon"/>
diff --git a/org.argeo.suite.e4/src/org/argeo/suite/e4/parts/AbstractSuiteDashboard.java b/org.argeo.suite.e4/src/org/argeo/suite/e4/parts/AbstractSuiteDashboard.java
new file mode 100644 (file)
index 0000000..9691901
--- /dev/null
@@ -0,0 +1,161 @@
+package org.argeo.suite.e4.parts;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import javax.jcr.Node;
+import javax.jcr.Repository;
+import javax.jcr.Session;
+
+import org.argeo.cms.ui.eclipse.forms.FormToolkit;
+import org.argeo.cms.util.CmsUtils;
+import org.argeo.connect.SystemAppService;
+import org.argeo.connect.resources.ResourcesService;
+import org.argeo.connect.ui.AppWorkbenchService;
+import org.argeo.connect.ui.ConnectUiStyles;
+import org.argeo.connect.ui.SystemWorkbenchService;
+import org.argeo.connect.util.ConnectJcrUtils;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.argeo.jcr.JcrUtils;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Link;
+
+/** Generic dashboard for Argeo Suite applications */
+public abstract class AbstractSuiteDashboard {
+
+       // DEPENDENCY INJECTION
+       private Repository repository;
+       private ResourcesService resourcesService;
+       private SystemAppService systemAppService;
+       private SystemWorkbenchService systemWorkbenchService;
+
+       private Session session;
+
+       // UI Objects
+       private FormToolkit toolkit;
+
+       public void init()  {
+               session = ConnectJcrUtils.login(repository);
+//             updateTooltip(input);
+       }
+
+//     private void updateTooltip(IEditorInput input) {
+//             if (input instanceof EntityEditorInput) {
+//                     EntityEditorInput sei = (EntityEditorInput) input;
+//                     sei.setTooltipText("My Dashboard");
+//             }
+//     }
+
+       /**
+        * Implementing classes must call super in order to create the correct form
+        * toolkit
+        */
+       @PostConstruct
+       public void createPartControl(Composite parent) {
+               toolkit = new FormToolkit(Display.getCurrent());
+               init();
+       }
+
+       // UTILS
+       protected Composite createGadgetCmp(Composite parent, int widthHint, int heightHint) {
+               Composite gadgetCmp = toolkit.createComposite(parent, SWT.BORDER);
+               GridData gd = new GridData(SWT.CENTER, SWT.CENTER, false, false);
+               gd.widthHint = widthHint;
+               gd.heightHint = heightHint;
+               gadgetCmp.setLayoutData(gd);
+               CmsUtils.style(gadgetCmp, ConnectUiStyles.GADGET_BOX);
+               return gadgetCmp;
+       }
+
+       protected Composite createGadgetTitleCmp(Composite parent, String title) {
+               Composite titleCmp = toolkit.createComposite(parent, SWT.BACKGROUND | SWT.INHERIT_NONE);
+               CmsUtils.style(titleCmp, ConnectUiStyles.GADGET_HEADER);
+               titleCmp.setBackground(null);
+               GridData gd = new GridData(SWT.FILL, SWT.TOP, true, false);
+               titleCmp.setLayoutData(gd);
+               titleCmp.setLayout(new GridLayout());
+
+               Label titleLbl = toolkit.createLabel(titleCmp, title + " ", SWT.BOLD);
+               CmsUtils.style(titleLbl, ConnectUiStyles.GADGET_HEADER);
+               titleLbl.setBackground(null);
+               return titleCmp;
+       }
+
+       protected Composite createGadgetBodyCmp(Composite parent) {
+               Composite bodyCmp = toolkit.createComposite(parent, SWT.BACKGROUND | SWT.INHERIT_NONE);
+               bodyCmp.setLayoutData(EclipseUiUtils.fillAll());
+               bodyCmp.setLayout(new GridLayout());
+               return bodyCmp;
+       }
+
+       protected Link createOpenEntityEditorLink(final AppWorkbenchService peopleUiService, Composite parent,
+                       final String label, final Node entity) {
+               Link link = new Link(parent, SWT.NONE);
+               link.setText("<a>" + label + "</a>");
+               link.setLayoutData(EclipseUiUtils.fillWidth());
+               link.addSelectionListener(new SelectionAdapter() {
+                       private static final long serialVersionUID = 1L;
+
+                       @Override
+                       public void widgetSelected(final SelectionEvent event) {
+                               // Map<String, String> params = new HashMap<String, String>();
+                               // params.put(ConnectEditor.PARAM_JCR_ID,
+                               // ConnectJcrUtils.getIdentifier(entity));
+                               // CommandUtils.callCommand(peopleUiService.getOpenEntityEditorCmdId(), params);
+                               peopleUiService.openEntityEditor(entity);
+                       }
+               });
+               return link;
+       }
+
+       // Life cycle
+       @PreDestroy
+       public void dispose() {
+               JcrUtils.logoutQuietly(session);
+       }
+
+
+       // Expose to implementing classes
+       protected Session getSession() {
+               return session;
+       }
+
+       public ResourcesService getResourcesService() {
+               return resourcesService;
+       }
+
+       protected SystemAppService getSystemAppService() {
+               return systemAppService;
+       }
+
+       protected SystemWorkbenchService getSystemWorkbenchService() {
+               return systemWorkbenchService;
+       }
+
+       protected FormToolkit getFormToolkit() {
+               return toolkit;
+       }
+
+       /* DEPENDENCY INJECTION */
+       public void setRepository(Repository repository) {
+               this.repository = repository;
+       }
+
+       public void setResourcesService(ResourcesService resourcesService) {
+               this.resourcesService = resourcesService;
+       }
+
+       public void setSystemAppService(SystemAppService systemAppService) {
+               this.systemAppService = systemAppService;
+       }
+
+       public void setSystemWorkbenchService(SystemWorkbenchService systemWorkbenchService) {
+               this.systemWorkbenchService = systemWorkbenchService;
+       }
+}
diff --git a/org.argeo.suite.e4/src/org/argeo/suite/e4/parts/DefaultDashboardEditor.java b/org.argeo.suite.e4/src/org/argeo/suite/e4/parts/DefaultDashboardEditor.java
new file mode 100644 (file)
index 0000000..d068575
--- /dev/null
@@ -0,0 +1,401 @@
+package org.argeo.suite.e4.parts;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.List;
+
+import javax.annotation.PostConstruct;
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.activities.ActivitiesNames;
+import org.argeo.activities.ActivitiesService;
+import org.argeo.activities.ui.TaskViewerContextMenu;
+import org.argeo.cms.auth.CurrentUser;
+import org.argeo.cms.util.CmsUtils;
+import org.argeo.connect.ConnectException;
+import org.argeo.connect.ConnectNames;
+import org.argeo.connect.ui.ConnectWorkbenchUtils;
+import org.argeo.connect.ui.Refreshable;
+import org.argeo.connect.util.ConnectJcrUtils;
+import org.argeo.eclipse.ui.EclipseUiUtils;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.node.NodeUtils;
+import org.argeo.tracker.TrackerNames;
+import org.argeo.tracker.TrackerService;
+import org.argeo.tracker.TrackerTypes;
+import org.argeo.tracker.core.TrackerUtils;
+import org.argeo.tracker.ui.TaskListLabelProvider;
+import org.argeo.tracker.ui.TaskVirtualListComposite;
+import org.eclipse.e4.ui.di.Focus;
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.layout.RowLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Link;
+
+/** Argeo Suite Default Dashboard */
+public class DefaultDashboardEditor extends AbstractSuiteDashboard implements Refreshable {
+       final static Log log = LogFactory.getLog(DefaultDashboardEditor.class);
+       // public final static String ID = AsUiPlugin.PLUGIN_ID +
+       // ".defaultDashboardEditor";
+
+       private ActivitiesService activitiesService;
+       private TrackerService trackerService;
+
+       private String datePattern = "dd MMM yyyy";
+
+       private Composite headerCmp;
+       private Composite taskListCmp;
+       private TaskVirtualListComposite tvlc;
+
+       @PostConstruct
+       public void createPartControl(Composite parent) {
+               super.createPartControl(parent);
+               parent.setLayout(EclipseUiUtils.noSpaceGridLayout());
+               Composite bodyCmp = new Composite(parent, SWT.NO_FOCUS);
+               bodyCmp.setLayoutData(EclipseUiUtils.fillAll());
+               bodyCmp.setLayout(new GridLayout());
+
+               headerCmp = new Composite(bodyCmp, SWT.NO_FOCUS);
+               headerCmp.setLayoutData(EclipseUiUtils.fillWidth());
+
+               taskListCmp = new Composite(bodyCmp, SWT.NO_FOCUS);
+               taskListCmp.setLayoutData(EclipseUiUtils.fillAll());
+               forceRefresh(null);
+       }
+
+       @Override
+       public void forceRefresh(Object object) {
+               CmsUtils.clear(headerCmp);
+               populateHeaderPart(headerCmp, NodeUtils.getUserHome(getSession()));
+
+               CmsUtils.clear(taskListCmp);
+               populateTaskListCmp(taskListCmp);
+
+               headerCmp.getParent().layout(true, true);
+       }
+
+       private void populateTaskListCmp(Composite parent) {
+               parent.setLayout(EclipseUiUtils.noSpaceGridLayout());
+               NodeIterator nit = activitiesService.getMyTasks(getSession(), true);
+               if (!nit.hasNext()) {
+                       Composite noTaskCmp = new Composite(parent, SWT.NO_FOCUS);
+                       noTaskCmp.setLayoutData(EclipseUiUtils.fillAll());
+                       noTaskCmp.setLayout(new GridLayout());
+
+                       Label noTaskLbl = new Label(noTaskCmp, SWT.CENTER);
+                       noTaskLbl.setText("<i> <big> You have no pending Task. </big> </i>");
+                       CmsUtils.markup(noTaskLbl);
+                       noTaskLbl.setLayoutData(new GridData(SWT.CENTER, SWT.BOTTOM, true, true));
+
+                       final Link createTaskLk = new Link(noTaskCmp, SWT.CENTER);
+                       createTaskLk.setText("<a> Create a task </a>");
+                       createTaskLk.setLayoutData(new GridData(SWT.CENTER, SWT.TOP, true, true));
+
+                       createTaskLk.addSelectionListener(new SelectionAdapter() {
+                               private static final long serialVersionUID = -9028457805156989935L;
+
+                               @Override
+                               public void widgetSelected(SelectionEvent e) {
+                                       String mainMixin = TrackerTypes.TRACKER_TASK;
+                                       String pathCreated = ConnectWorkbenchUtils.createAndConfigureEntity(createTaskLk.getShell(),
+                                                       getSession(), getSystemAppService(), getSystemWorkbenchService(), mainMixin);
+                                       if (EclipseUiUtils.notEmpty(pathCreated))
+                                               forceRefresh(null);
+                               }
+                       });
+
+               } else {
+                       TaskListLabelProvider labelProvider = new TaskListLabelProvider(trackerService);
+                       tvlc = new TaskVirtualListComposite(parent, SWT.NO_FOCUS, (ILabelProvider) labelProvider, 54);
+                       tvlc.setLayoutData(EclipseUiUtils.fillAll());
+                       final TableViewer viewer = tvlc.getTableViewer();
+                       viewer.setInput(JcrUtils.nodeIteratorToList(nit).toArray());
+                       final TaskViewerContextMenu contextMenu = new TaskViewerContextMenu(viewer, getSession(),
+                                       activitiesService) {
+                               @Override
+                               public boolean performAction(String actionId) {
+                                       boolean hasChanged = super.performAction(actionId);
+                                       if (hasChanged) {
+                                               viewer.getTable().setFocus();
+                                               forceRefresh(null);
+                                               // NodeIterator nit =
+                                               // activitiesService.getMyTasks(getSession(), true);
+                                               // viewer.setInput(JcrUtils.nodeIteratorToList(nit).toArray());
+                                       }
+                                       return hasChanged;
+                               }
+                       };
+                       viewer.getTable().addMouseListener(new MouseAdapter() {
+                               private static final long serialVersionUID = 6737579410648595940L;
+
+                               @Override
+                               public void mouseDown(MouseEvent e) {
+                                       if (e.button == 3) {
+                                               // contextMenu.setCurrFolderPath(currDisplayedFolder);
+                                               contextMenu.show(viewer.getTable(), new Point(e.x, e.y),
+                                                               (IStructuredSelection) viewer.getSelection());
+                                       }
+                               }
+                       });
+
+               }
+       }
+
+       private boolean isOverdue(Node node, String propName) {
+               try {
+                       Calendar now = GregorianCalendar.getInstance();
+                       return node.hasProperty(propName) && node.getProperty(propName).getDate().before(now);
+               } catch (RepositoryException e) {
+                       throw new ConnectException("Cannot check overdue status with property " + propName + " on " + node, e);
+               }
+       }
+
+       private void populateHeaderPart(Composite bodyCmp, Node context) {
+               bodyCmp.setLayout(EclipseUiUtils.noSpaceGridLayout(new GridLayout(2, true)));
+
+               Composite leftCmp = new Composite(bodyCmp, SWT.NO_FOCUS);
+               leftCmp.setLayout(new GridLayout());
+               leftCmp.setLayoutData(EclipseUiUtils.fillWidth());
+               Composite rightCmp = new Composite(bodyCmp, SWT.NO_FOCUS);
+               rightCmp.setLayout(new GridLayout());
+               rightCmp.setLayoutData(EclipseUiUtils.fillWidth());
+
+               // Title
+               Label titleLbl = new Label(leftCmp, SWT.WRAP | SWT.LEAD);
+               CmsUtils.markup(titleLbl);
+               String titleStr = "<big><b> Hello " + CurrentUser.getDisplayName() + " </b></big>";
+               titleLbl.setText(titleStr);
+               GridData gd = new GridData(SWT.TOP, SWT.BOTTOM, false, false);
+               gd.verticalIndent = 5;
+               gd.horizontalIndent = 10;
+               titleLbl.setLayoutData(gd);
+
+               NodeIterator nit = activitiesService.getMyTasks(getSession(), true);
+               if (nit.hasNext()) {
+                       List<Node> overdueTasks = new ArrayList<>();
+                       while (nit.hasNext()) {
+                               Node currNode = nit.nextNode();
+                               if (isOverdue(currNode, ActivitiesNames.ACTIVITIES_DUE_DATE))
+                                       overdueTasks.add(currNode);
+                       }
+                       if (!overdueTasks.isEmpty()) {
+                               Composite overdueCmp = new Composite(leftCmp, SWT.NO_FOCUS);
+                               long size = overdueTasks.size();
+                               String overdueStr = "You have " + size + " overdue task" + (size > 1 ? "s" : "") + ": ";
+                               populateMuliValueClickableList(overdueCmp, overdueTasks.toArray(new Node[0]), new TaskLp(), overdueStr);
+                       }
+               }
+
+               nit = trackerService.getMyMilestones(getSession(), true);
+               List<Node> openMilestones = new ArrayList<>();
+
+               if (nit.hasNext()) {
+                       List<Node> overdueMilestones = new ArrayList<>();
+                       while (nit.hasNext()) {
+                               Node currNode = nit.nextNode();
+                               openMilestones.add(currNode);
+                               if (isOverdue(currNode, TrackerNames.TRACKER_TARGET_DATE))
+                                       overdueMilestones.add(currNode);
+                       }
+                       if (!overdueMilestones.isEmpty()) {
+                               Composite overdueCmp = new Composite(leftCmp, SWT.NO_FOCUS);
+                               long size = overdueMilestones.size();
+                               String overdueStr = "You have " + size + " overdue milestone" + (size > 1 ? "s" : "") + ": ";
+                               populateMuliValueClickableList(overdueCmp, overdueMilestones.toArray(new Node[0]), new MilestoneLp(),
+                                               overdueStr);
+                       }
+               }
+
+               // My projects
+               List<Node> openProjects = JcrUtils.nodeIteratorToList(trackerService.getMyProjects(getSession(), true));
+               if (!openProjects.isEmpty()) {
+                       Group myProjectsGp = new Group(rightCmp, SWT.NO_FOCUS);
+                       myProjectsGp.setText("My open projects");
+                       myProjectsGp.setLayoutData(EclipseUiUtils.fillWidth());
+                       populateMuliValueClickableList(myProjectsGp, openProjects.toArray(new Node[0]), new ProjectLp(), null);
+               }
+
+               // My Milestones
+               if (!openMilestones.isEmpty()) {
+                       Group myMilestoneGp = new Group(rightCmp, SWT.NO_FOCUS);
+                       myMilestoneGp.setText("My open milestones");
+                       myMilestoneGp.setLayoutData(EclipseUiUtils.fillWidth());
+                       populateMuliValueClickableList(myMilestoneGp, openMilestones.toArray(new Node[0]), new MilestoneLp(), null);
+               }
+       }
+
+       private class ProjectLp extends ColumnLabelProvider {
+               private static final long serialVersionUID = 7231233932794865555L;
+
+               @Override
+               public String getText(Object element) {
+                       Node project = (Node) element;
+
+                       String percent;
+                       NodeIterator nit = TrackerUtils.getIssues(project, null, null, null, true);
+                       long openNb = nit.getSize();
+
+                       nit = TrackerUtils.getIssues(project, null, null, null, false);
+                       long allNb = nit.getSize();
+
+                       if (allNb < 1)
+                               percent = "empty";
+                       else {
+                               double num = allNb - openNb;
+                               double result = num / allNb * 100;
+                               percent = String.format("%.1f", result) + "% done";
+                       }
+                       StringBuilder builder = new StringBuilder();
+                       builder.append("<a>").append(ConnectJcrUtils.get(project, Property.JCR_TITLE)).append("</a>");
+                       builder.append(" (").append(percent).append(")");
+
+                       return builder.toString();
+               }
+       }
+
+       private class MilestoneLp extends ColumnLabelProvider {
+               private static final long serialVersionUID = 7231233932794865555L;
+
+               @Override
+               public String getText(Object element) {
+                       Node milestone = (Node) element;
+                       Node project = TrackerUtils.getRelatedProject(trackerService, milestone);
+                       String dueDate = ConnectJcrUtils.getDateFormattedAsString(milestone, TrackerNames.TRACKER_TARGET_DATE,
+                                       datePattern);
+
+                       String percent;
+                       String propName = TrackerNames.TRACKER_MILESTONE_UID;
+                       String muid = ConnectJcrUtils.get(milestone, ConnectNames.CONNECT_UID);
+                       NodeIterator nit = TrackerUtils.getIssues(project, null, propName, muid, true);
+                       long openNb = nit.getSize();
+
+                       nit = TrackerUtils.getIssues(project, null, propName, muid, false);
+                       long allNb = nit.getSize();
+
+                       if (allNb < 1)
+                               percent = "empty";
+                       else {
+                               double num = allNb - openNb;
+                               double result = num / allNb * 100;
+                               percent = String.format("%.1f", result) + "% done";
+                       }
+                       StringBuilder builder = new StringBuilder();
+                       builder.append("<a>").append(ConnectJcrUtils.get(milestone, Property.JCR_TITLE)).append("</a>");
+                       builder.append(" (");
+                       if (EclipseUiUtils.notEmpty(dueDate))
+                               builder.append("due to ").append(dueDate).append(", ");
+
+                       builder.append(percent).append(")");
+                       return builder.toString();
+               }
+
+               @Override
+               public Color getForeground(Object element) {
+                       Node milestone = (Node) element;
+                       Calendar dueDate = ConnectJcrUtils.getDateValue(milestone, TrackerNames.TRACKER_TARGET_DATE);
+                       if (dueDate != null && dueDate.before(Calendar.getInstance()))
+                               return Display.getCurrent().getSystemColor(SWT.COLOR_RED);
+                       return null;
+               }
+       }
+
+       private class TaskLp extends ColumnLabelProvider {
+               private static final long serialVersionUID = 7231233932794865555L;
+
+               @Override
+               public String getText(Object element) {
+                       Node task = (Node) element;
+                       String dueDate = ConnectJcrUtils.getDateFormattedAsString(task, ActivitiesNames.ACTIVITIES_DUE_DATE,
+                                       datePattern);
+
+                       StringBuilder builder = new StringBuilder();
+                       builder.append("<a>").append(ConnectJcrUtils.get(task, Property.JCR_TITLE)).append("</a>");
+                       if (EclipseUiUtils.notEmpty(dueDate))
+                               builder.append(" (").append("due to ").append(dueDate).append(")");
+                       return builder.toString();
+               }
+
+               @Override
+               public Color getForeground(Object element) {
+                       Node milestone = (Node) element;
+                       Calendar dueDate = ConnectJcrUtils.getDateValue(milestone, TrackerNames.TRACKER_TARGET_DATE);
+                       if (dueDate != null && dueDate.before(Calendar.getInstance()))
+                               return Display.getCurrent().getSystemColor(SWT.COLOR_RED);
+                       return null;
+               }
+       }
+
+       @Focus
+       public void setFocus() {
+               // refreshDocListGadget();
+       }
+
+       public void setActivitiesService(ActivitiesService activitiesService) {
+               this.activitiesService = activitiesService;
+       }
+
+       public void setTrackerService(TrackerService trackerService) {
+               this.trackerService = trackerService;
+       }
+
+       // LOCAL HELPERS
+       private void populateMuliValueClickableList(Composite parent, Node[] nodes, ColumnLabelProvider lp,
+                       String listLabel) {
+               CmsUtils.clear(parent);
+               RowLayout rl = new RowLayout(SWT.HORIZONTAL | SWT.WRAP);
+               rl.wrap = true;
+               rl.marginLeft = rl.marginTop = rl.marginBottom = 0;
+               rl.marginRight = 8;
+               parent.setLayout(rl);
+
+               if (EclipseUiUtils.notEmpty(listLabel)) {
+                       Link link = new Link(parent, SWT.NONE);
+                       link.setText(listLabel);
+                       link.setFont(EclipseUiUtils.getBoldFont(parent));
+               }
+
+               int i = 1;
+               for (Node node : nodes) {
+                       Link link = new Link(parent, SWT.NONE);
+                       CmsUtils.markup(link);
+                       link.setText(lp.getText(node) + (i != nodes.length ? ", " : ""));
+                       i++;
+                       // Color fc = lp.getForeground(node);
+                       // if (fc != null)
+                       // link.setForeground(fc);
+
+                       link.addSelectionListener(new SelectionAdapter() {
+                               private static final long serialVersionUID = 1L;
+
+                               @Override
+                               public void widgetSelected(final SelectionEvent event) {
+                                       // CommandUtils.callCommand(getSystemWorkbenchService().getOpenEntityEditorCmdId(),
+                                       // ConnectEditor.PARAM_JCR_ID, ConnectJcrUtils.getIdentifier(node));
+                                       getSystemWorkbenchService().openEntityEditor(node);
+                               }
+                       });
+               }
+       }
+
+}