1 package org
.argeo
.app
.ui
;
3 import static org
.argeo
.eclipse
.ui
.EclipseUiUtils
.notEmpty
;
9 import javax
.jcr
.NodeIterator
;
10 import javax
.jcr
.RepositoryException
;
11 import javax
.jcr
.Session
;
12 import javax
.jcr
.observation
.Event
;
13 import javax
.jcr
.observation
.EventIterator
;
14 import javax
.jcr
.observation
.EventListener
;
15 import javax
.jcr
.query
.Query
;
16 import javax
.jcr
.query
.QueryResult
;
18 import org
.argeo
.app
.api
.EntityType
;
19 import org
.argeo
.app
.jcr
.XPathUtils
;
20 import org
.argeo
.app
.ui
.widgets
.DelayedText
;
21 import org
.argeo
.app
.ux
.SuiteIcon
;
22 import org
.argeo
.app
.ux
.SuiteUxEvent
;
23 import org
.argeo
.cms
.swt
.CmsSwtTheme
;
24 import org
.argeo
.cms
.swt
.CmsSwtUtils
;
25 import org
.argeo
.cms
.ui
.CmsUiProvider
;
26 import org
.argeo
.eclipse
.ui
.EclipseUiUtils
;
27 import org
.argeo
.jcr
.Jcr
;
28 import org
.argeo
.jcr
.JcrUtils
;
29 import org
.eclipse
.jface
.layout
.TableColumnLayout
;
30 import org
.eclipse
.jface
.viewers
.ColumnLabelProvider
;
31 import org
.eclipse
.jface
.viewers
.ColumnWeightData
;
32 import org
.eclipse
.jface
.viewers
.DoubleClickEvent
;
33 import org
.eclipse
.jface
.viewers
.IDoubleClickListener
;
34 import org
.eclipse
.jface
.viewers
.ILabelProvider
;
35 import org
.eclipse
.jface
.viewers
.ISelectionChangedListener
;
36 import org
.eclipse
.jface
.viewers
.IStructuredContentProvider
;
37 import org
.eclipse
.jface
.viewers
.SelectionChangedEvent
;
38 import org
.eclipse
.jface
.viewers
.TableViewer
;
39 import org
.eclipse
.jface
.viewers
.Viewer
;
40 import org
.eclipse
.swt
.SWT
;
41 import org
.eclipse
.swt
.events
.KeyEvent
;
42 import org
.eclipse
.swt
.events
.KeyListener
;
43 import org
.eclipse
.swt
.layout
.GridData
;
44 import org
.eclipse
.swt
.layout
.GridLayout
;
45 import org
.eclipse
.swt
.widgets
.Composite
;
46 import org
.eclipse
.swt
.widgets
.Control
;
47 import org
.eclipse
.swt
.widgets
.Table
;
48 import org
.eclipse
.swt
.widgets
.TableColumn
;
49 import org
.eclipse
.swt
.widgets
.Text
;
50 import org
.eclipse
.swt
.widgets
.ToolBar
;
51 import org
.eclipse
.swt
.widgets
.ToolItem
;
53 /** List recent items. */
54 public class RecentItems
implements CmsUiProvider
{
55 private final static int SEARCH_TEXT_DELAY
= 800;
56 private final static int SEARCH_DEFAULT_LIMIT
= 100;
58 private CmsSwtTheme theme
;
60 private String entityType
;
62 static enum Property
{
67 public Control
createUi(Composite parent
, Node context
) throws RepositoryException
{
68 theme
= CmsSwtUtils
.getCmsTheme(parent
);
69 parent
.setLayout(new GridLayout());
70 // parent.setLayout(CmsUiUtils.noSpaceGridLayout());
71 parent
.setLayout(new GridLayout());
73 // Composite top = new Composite(parent, SWT.BORDER);
74 // CmsUiUtils.style(top, SuiteStyle.recentItems);
75 // top.setLayoutData(CmsUiUtils.fillWidth());
76 // top.setLayout(CmsUiUtils.noSpaceGridLayout(2));
77 // Label lbl = new Label(top, SWT.FLAT);
78 // lbl.setLayoutData(CmsUiUtils.fillWidth());
79 // lbl.setText(SuiteMsg.recentItems.lead());
80 // CmsUiUtils.style(lbl, SuiteStyle.recentItems);
82 // ToolBar topToolBar = new ToolBar(top, SWT.NONE);
83 // ToolItem addItem = new ToolItem(topToolBar, SWT.FLAT);
84 //// CmsUiUtils.style(addItem, SuiteStyle.recentItems);
85 // addItem.setImage(SuiteIcon.add.getSmallIcon(theme));
89 SingleEntityViewer entityViewer
= new SingleEntityViewer(parent
, SWT
.NONE
, context
.getSession());
90 entityViewer
.createUi();
91 entityViewer
.getViewer().getTable().setLayoutData(CmsSwtUtils
.fillAll());
93 Composite bottom
= new Composite(parent
, SWT
.NONE
);
94 bottom
.setLayoutData(CmsSwtUtils
.fillWidth());
95 bottom
.setLayout(CmsSwtUtils
.noSpaceGridLayout());
96 ToolBar bottomToolBar
= new ToolBar(bottom
, SWT
.NONE
);
97 bottomToolBar
.setLayoutData(new GridData(SWT
.END
, SWT
.FILL
, true, false));
98 ToolItem deleteItem
= new ToolItem(bottomToolBar
, SWT
.FLAT
);
99 deleteItem
.setEnabled(false);
100 // CmsUiUtils.style(deleteItem, SuiteStyle.recentItems);
101 deleteItem
.setImage(theme
.getSmallIcon(SuiteIcon
.delete
));
102 ToolItem addItem
= new ToolItem(bottomToolBar
, SWT
.FLAT
);
103 addItem
.setImage(theme
.getSmallIcon(SuiteIcon
.add
));
104 entityViewer
.getViewer().addDoubleClickListener(new IDoubleClickListener() {
107 public void doubleClick(DoubleClickEvent event
) {
108 Node node
= (Node
) entityViewer
.getViewer().getStructuredSelection().getFirstElement();
110 CmsSwtUtils
.getCmsView(parent
).sendEvent(SuiteUxEvent
.openNewPart
.topic(),
111 SuiteUiUtils
.eventProperties(node
));
115 entityViewer
.getViewer().addSelectionChangedListener(new ISelectionChangedListener() {
116 public void selectionChanged(SelectionChangedEvent event
) {
117 Node node
= (Node
) entityViewer
.getViewer().getStructuredSelection().getFirstElement();
119 CmsSwtUtils
.getCmsView(parent
).sendEvent(SuiteUxEvent
.refreshPart
.topic(),
120 SuiteUiUtils
.eventProperties(node
));
121 deleteItem
.setEnabled(true);
123 deleteItem
.setEnabled(false);
128 return entityViewer
.filterTxt
;
132 public void init(Map
<String
, String
> properties
) {
133 // TODO manage multiple entities
134 entityType
= properties
.get(Property
.entityTypes
.name());
137 class SingleEntityViewer
{
143 public SingleEntityViewer(Composite parent
, int style
, Session session
) {
144 this.parent
= parent
;
145 this.session
= session
;
148 public void createUi() {
150 addFilterPanel(parent
);
151 viewer
= createListPart(parent
, new SingleEntityLabelProvider());
152 refreshFilteredList();
155 String
[] nodeTypes
= entityType
!= null && entityType
.contains(":") ?
new String
[] { entityType
}
157 session
.getWorkspace().getObservationManager().addEventListener(new EventListener() {
160 public void onEvent(EventIterator events
) {
161 parent
.getDisplay().asyncExec(() -> refreshFilteredList());
163 }, Event
.PROPERTY_CHANGED
| Event
.NODE_ADDED
| Event
.NODE_REMOVED
| Event
.PROPERTY_ADDED
, "/", true,
164 null, nodeTypes
, false);
165 } catch (RepositoryException e
) {
166 throw new IllegalStateException("Cannot add JCR observer", e
);
171 private void addFilterPanel(Composite parent
) {
172 // Use a delayed text: the query won't be done until the user stop
174 int style
= SWT
.BORDER
| SWT
.SEARCH
| SWT
.ICON_CANCEL
;
175 DelayedText delayedText
= new DelayedText(parent
, style
, SEARCH_TEXT_DELAY
);
176 filterTxt
= delayedText
.getText();
177 filterTxt
.setLayoutData(EclipseUiUtils
.fillWidth());
179 // final ServerPushSession pushSession = new ServerPushSession();
180 delayedText
.addListener((s
) -> refreshFilteredList());
181 // delayedText.addDelayedModifyListener(null, new ModifyListener() {
182 // private static final long serialVersionUID = 5003010530960334977L;
184 // public void modifyText(ModifyEvent event) {
185 // delayedText.getText().getDisplay().asyncExec(new Runnable() {
187 // public void run() {
188 // refreshFilteredList();
191 // // pushSession.stop();
195 // Jump to the first item of the list using the down arrow
196 filterTxt
.addKeyListener(new KeyListener() {
197 private static final long serialVersionUID
= -4523394262771183968L;
200 public void keyReleased(KeyEvent e
) {
204 public void keyPressed(KeyEvent e
) {
205 // boolean shiftPressed = (e.stateMask & SWT.SHIFT) != 0;
206 // boolean altPressed = (e.stateMask & SWT.ALT) != 0;
207 if (e
.keyCode
== SWT
.ARROW_DOWN
|| e
.keyCode
== SWT
.TAB
) {
208 // Object first = entityViewer.getElementAt(0);
209 // if (first != null) {
210 // entityViewer.getTable().setFocus();
211 // entityViewer.setSelection(new StructuredSelection(first), true);
218 // parent.addDisposeListener((e) -> {
219 // delayedText.close();
223 protected TableViewer
createListPart(Composite parent
, ILabelProvider labelProvider
) {
224 // parent.setLayout(new GridLayout());
225 // parent.setLayout(CmsUiUtils.noSpaceGridLayout());
227 Composite tableComposite
= new Composite(parent
, SWT
.NONE
);
228 GridData gd
= new GridData(GridData
.HORIZONTAL_ALIGN_FILL
| GridData
.GRAB_VERTICAL
229 | GridData
.VERTICAL_ALIGN_FILL
| GridData
.GRAB_HORIZONTAL
);
230 tableComposite
.setLayoutData(gd
);
232 TableViewer viewer
= new TableViewer(tableComposite
, SWT
.SINGLE
| SWT
.H_SCROLL
| SWT
.V_SCROLL
| SWT
.BORDER
);
233 viewer
.setLabelProvider(labelProvider
);
235 TableColumn singleColumn
= new TableColumn(viewer
.getTable(), SWT
.V_SCROLL
);
236 TableColumnLayout tableColumnLayout
= new TableColumnLayout();
237 tableColumnLayout
.setColumnData(singleColumn
, new ColumnWeightData(85));
238 tableComposite
.setLayout(tableColumnLayout
);
240 // Corresponding table & style
241 Table table
= viewer
.getTable();
242 // Listener[] mouseDownListeners = table.getListeners(SWT.MouseDown);
243 // for (Listener listener : table.getListeners(SWT.MouseDown))
244 // table.removeListener(SWT.MouseDown, listener);
245 // for (Listener listener : table.getListeners(SWT.MouseUp))
246 // table.removeListener(SWT.MouseUp, listener);
247 // for (Listener listener : table.getListeners(SWT.MouseDoubleClick))
248 // table.removeListener(SWT.MouseDoubleClick, listener);
250 // table.addMouseListener(new MouseListener() {
253 // public void mouseUp(MouseEvent e) {
254 // System.out.println("Mouse up: "+e);
258 // public void mouseDown(MouseEvent e) {
259 // System.out.println("Mouse down: "+e);
263 // public void mouseDoubleClick(MouseEvent e) {
264 // System.out.println("Mouse double: "+e);
268 table
.setLinesVisible(true);
269 table
.setHeaderVisible(false);
270 // CmsUiUtils.markup(table);
271 // CmsUiUtils.setItemHeight(table, 26);
273 viewer
.setContentProvider(new BasicNodeListContentProvider());
277 // public boolean setFocus() {
278 // refreshFilteredList();
279 // return parent.setFocus();
282 public void forceRefresh(Object object
) {
283 refreshFilteredList();
286 protected void refreshFilteredList() {
288 String filter
= filterTxt
.getText();
289 // Prevents the query on the full repository
290 // if (isEmpty(filter)) {
291 // entityViewer.setInput(null);
296 String xpathQueryStr
;
297 if (entityType
!= null) {
298 int indexColumn
= entityType
.indexOf(':');
299 if (indexColumn
> 0) {// JCR node type
300 xpathQueryStr
= "//element(*, " + entityType
+ ") order by @jcr:created descending";
302 xpathQueryStr
= entityType
.contains(":") ?
"//element(*, " + entityType
+ ")"
303 : "//element(*, " + EntityType
.entity
.get() + ")[@entity:type='" + entityType
+ "']";
306 xpathQueryStr
= "//element(*, " + EntityType
.entity
.get() + ")";
308 // String xpathQueryStr = "//element(*, " + ConnectTypes.CONNECT_ENTITY + ")";
309 String xpathFilter
= XPathUtils
.getFreeTextConstraint(filter
);
310 if (notEmpty(xpathFilter
))
311 xpathQueryStr
+= "[" + xpathFilter
+ "]";
313 // long begin = System.currentTimeMillis();
314 // session.refresh(false);
315 Query xpathQuery
= XPathUtils
.createQuery(session
, xpathQueryStr
);
317 xpathQuery
.setLimit(SEARCH_DEFAULT_LIMIT
);
318 QueryResult result
= xpathQuery
.execute();
320 NodeIterator nit
= result
.getNodes();
321 viewer
.setInput(JcrUtils
.nodeIteratorToList(nit
));
322 // if (log.isTraceEnabled()) {
323 // long end = System.currentTimeMillis();
324 // log.trace("Quick Search - Found: " + nit.getSize() + " in " + (end - begin)
325 // + " ms by executing XPath query (" + xpathQueryStr + ").");
327 } catch (RepositoryException e
) {
328 throw new IllegalStateException("Unable to list entities", e
);
332 public TableViewer
getViewer() {
336 class SingleEntityLabelProvider
extends ColumnLabelProvider
{
337 private static final long serialVersionUID
= -2209337675781795677L;
340 public String
getText(Object element
) {
341 return Jcr
.getTitle((Node
) element
);
346 class BasicNodeListContentProvider
implements IStructuredContentProvider
{
347 private static final long serialVersionUID
= 1L;
348 // keep a cache of the Nodes in the content provider to be able to
349 // manage long request
350 private List
<Node
> nodes
;
352 public void dispose() {
355 /** Expects a list of nodes as a new input */
356 @SuppressWarnings("unchecked")
357 public void inputChanged(Viewer viewer
, Object oldInput
, Object newInput
) {
358 nodes
= (List
<Node
>) newInput
;
361 public Object
[] getElements(Object arg0
) {
362 return nodes
.toArray();