]> git.argeo.org Git - gpl/argeo-suite.git/blob - ui/RecentItems.java
Prepare next development cycle
[gpl/argeo-suite.git] / ui / RecentItems.java
1 package org.argeo.app.ui;
2
3 import static org.argeo.eclipse.ui.EclipseUiUtils.notEmpty;
4
5 import java.util.List;
6 import java.util.Map;
7
8 import javax.jcr.Node;
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;
17
18 import org.argeo.api.cms.CmsTheme;
19 import org.argeo.app.api.EntityType;
20 import org.argeo.app.core.XPathUtils;
21 import org.argeo.app.ui.widgets.DelayedText;
22 import org.argeo.cms.swt.CmsSwtUtils;
23 import org.argeo.cms.ui.CmsUiProvider;
24 import org.argeo.eclipse.ui.EclipseUiUtils;
25 import org.argeo.jcr.Jcr;
26 import org.argeo.jcr.JcrUtils;
27 import org.eclipse.jface.layout.TableColumnLayout;
28 import org.eclipse.jface.viewers.ColumnLabelProvider;
29 import org.eclipse.jface.viewers.ColumnWeightData;
30 import org.eclipse.jface.viewers.DoubleClickEvent;
31 import org.eclipse.jface.viewers.IDoubleClickListener;
32 import org.eclipse.jface.viewers.ILabelProvider;
33 import org.eclipse.jface.viewers.ISelectionChangedListener;
34 import org.eclipse.jface.viewers.IStructuredContentProvider;
35 import org.eclipse.jface.viewers.SelectionChangedEvent;
36 import org.eclipse.jface.viewers.TableViewer;
37 import org.eclipse.jface.viewers.Viewer;
38 import org.eclipse.swt.SWT;
39 import org.eclipse.swt.events.KeyEvent;
40 import org.eclipse.swt.events.KeyListener;
41 import org.eclipse.swt.events.ModifyEvent;
42 import org.eclipse.swt.events.ModifyListener;
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;
52
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;
57
58 private CmsTheme theme;
59
60 private String entityType;
61
62 static enum Property {
63 entityTypes;
64 }
65
66 @Override
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());
72
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);
81 //
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));
86
87 if (context == null)
88 return null;
89 SingleEntityViewer entityViewer = new SingleEntityViewer(parent, SWT.NONE, context.getSession());
90 entityViewer.createUi();
91 entityViewer.getViewer().getTable().setLayoutData(CmsSwtUtils.fillAll());
92
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(SuiteIcon.delete.getSmallIcon(theme));
102 ToolItem addItem = new ToolItem(bottomToolBar, SWT.FLAT);
103 addItem.setImage(SuiteIcon.add.getSmallIcon(theme));
104 entityViewer.getViewer().addDoubleClickListener(new IDoubleClickListener() {
105
106 @Override
107 public void doubleClick(DoubleClickEvent event) {
108 Node node = (Node) entityViewer.getViewer().getStructuredSelection().getFirstElement();
109 if (node != null)
110 CmsSwtUtils.getCmsView(parent).sendEvent(SuiteEvent.openNewPart.topic(),
111 SuiteEvent.eventProperties(node));
112
113 }
114 });
115 entityViewer.getViewer().addSelectionChangedListener(new ISelectionChangedListener() {
116 public void selectionChanged(SelectionChangedEvent event) {
117 Node node = (Node) entityViewer.getViewer().getStructuredSelection().getFirstElement();
118 if (node != null) {
119 CmsSwtUtils.getCmsView(parent).sendEvent(SuiteEvent.refreshPart.topic(),
120 SuiteEvent.eventProperties(node));
121 deleteItem.setEnabled(true);
122 } else {
123 deleteItem.setEnabled(false);
124 }
125 }
126 });
127
128 return entityViewer.filterTxt;
129
130 }
131
132 public void init(Map<String, String> properties) {
133 // TODO manage multiple entities
134 entityType = properties.get(Property.entityTypes.name());
135 }
136
137 class SingleEntityViewer {
138 Composite parent;
139 Text filterTxt;
140 TableViewer viewer;
141 Session session;
142
143 public SingleEntityViewer(Composite parent, int style, Session session) {
144 this.parent = parent;
145 this.session = session;
146 }
147
148 public void createUi() {
149 // MainLayout
150 addFilterPanel(parent);
151 viewer = createListPart(parent, new SingleEntityLabelProvider());
152 refreshFilteredList();
153
154 try {
155 String[] nodeTypes = entityType != null && entityType.contains(":") ? new String[] { entityType }
156 : null;
157 session.getWorkspace().getObservationManager().addEventListener(new EventListener() {
158
159 @Override
160 public void onEvent(EventIterator events) {
161 parent.getDisplay().asyncExec(() -> refreshFilteredList());
162 }
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);
167 }
168
169 }
170
171 private void addFilterPanel(Composite parent) {
172 // Use a delayed text: the query won't be done until the user stop
173 // typing for 800ms
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());
178
179 // final ServerPushSession pushSession = new ServerPushSession();
180 delayedText.addDelayedModifyListener(null, new ModifyListener() {
181 private static final long serialVersionUID = 5003010530960334977L;
182
183 public void modifyText(ModifyEvent event) {
184 delayedText.getText().getDisplay().asyncExec(new Runnable() {
185 @Override
186 public void run() {
187 refreshFilteredList();
188 }
189 });
190 // pushSession.stop();
191 }
192 });
193
194 // Jump to the first item of the list using the down arrow
195 filterTxt.addKeyListener(new KeyListener() {
196 private static final long serialVersionUID = -4523394262771183968L;
197
198 @Override
199 public void keyReleased(KeyEvent e) {
200 }
201
202 @Override
203 public void keyPressed(KeyEvent e) {
204 // boolean shiftPressed = (e.stateMask & SWT.SHIFT) != 0;
205 // boolean altPressed = (e.stateMask & SWT.ALT) != 0;
206 if (e.keyCode == SWT.ARROW_DOWN || e.keyCode == SWT.TAB) {
207 // Object first = entityViewer.getElementAt(0);
208 // if (first != null) {
209 // entityViewer.getTable().setFocus();
210 // entityViewer.setSelection(new StructuredSelection(first), true);
211 // }
212 e.doit = false;
213 }
214 }
215 });
216
217 parent.addDisposeListener((e) -> {
218 delayedText.close();
219 });
220 }
221
222 protected TableViewer createListPart(Composite parent, ILabelProvider labelProvider) {
223 // parent.setLayout(new GridLayout());
224 // parent.setLayout(CmsUiUtils.noSpaceGridLayout());
225
226 Composite tableComposite = new Composite(parent, SWT.NONE);
227 GridData gd = new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_VERTICAL
228 | GridData.VERTICAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL);
229 tableComposite.setLayoutData(gd);
230
231 TableViewer viewer = new TableViewer(tableComposite, SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
232 viewer.setLabelProvider(labelProvider);
233
234 TableColumn singleColumn = new TableColumn(viewer.getTable(), SWT.V_SCROLL);
235 TableColumnLayout tableColumnLayout = new TableColumnLayout();
236 tableColumnLayout.setColumnData(singleColumn, new ColumnWeightData(85));
237 tableComposite.setLayout(tableColumnLayout);
238
239 // Corresponding table & style
240 Table table = viewer.getTable();
241 // Listener[] mouseDownListeners = table.getListeners(SWT.MouseDown);
242 // for (Listener listener : table.getListeners(SWT.MouseDown))
243 // table.removeListener(SWT.MouseDown, listener);
244 // for (Listener listener : table.getListeners(SWT.MouseUp))
245 // table.removeListener(SWT.MouseUp, listener);
246 // for (Listener listener : table.getListeners(SWT.MouseDoubleClick))
247 // table.removeListener(SWT.MouseDoubleClick, listener);
248 //
249 // table.addMouseListener(new MouseListener() {
250 //
251 // @Override
252 // public void mouseUp(MouseEvent e) {
253 // System.out.println("Mouse up: "+e);
254 // }
255 //
256 // @Override
257 // public void mouseDown(MouseEvent e) {
258 // System.out.println("Mouse down: "+e);
259 // }
260 //
261 // @Override
262 // public void mouseDoubleClick(MouseEvent e) {
263 // System.out.println("Mouse double: "+e);
264 //
265 // }
266 // });
267 table.setLinesVisible(true);
268 table.setHeaderVisible(false);
269 // CmsUiUtils.markup(table);
270 // CmsUiUtils.setItemHeight(table, 26);
271
272 viewer.setContentProvider(new BasicNodeListContentProvider());
273 return viewer;
274 }
275
276 // public boolean setFocus() {
277 // refreshFilteredList();
278 // return parent.setFocus();
279 // }
280
281 public void forceRefresh(Object object) {
282 refreshFilteredList();
283 }
284
285 protected void refreshFilteredList() {
286 try {
287 String filter = filterTxt.getText();
288 // Prevents the query on the full repository
289 // if (isEmpty(filter)) {
290 // entityViewer.setInput(null);
291 // return;
292 // }
293
294 // XPATH Query
295 String xpathQueryStr;
296 if (entityType != null) {
297 int indexColumn = entityType.indexOf(':');
298 if (indexColumn > 0) {// JCR node type
299 xpathQueryStr = "//element(*, " + entityType + ") order by @jcr:created descending";
300 } else {
301 xpathQueryStr = entityType.contains(":") ? "//element(*, " + entityType + ")"
302 : "//element(*, " + EntityType.entity.get() + ")[@entity:type='" + entityType + "']";
303 }
304 } else {
305 xpathQueryStr = "//element(*, " + EntityType.entity.get() + ")";
306 }
307 // String xpathQueryStr = "//element(*, " + ConnectTypes.CONNECT_ENTITY + ")";
308 String xpathFilter = XPathUtils.getFreeTextConstraint(filter);
309 if (notEmpty(xpathFilter))
310 xpathQueryStr += "[" + xpathFilter + "]";
311
312 // long begin = System.currentTimeMillis();
313 // session.refresh(false);
314 Query xpathQuery = XPathUtils.createQuery(session, xpathQueryStr);
315
316 xpathQuery.setLimit(SEARCH_DEFAULT_LIMIT);
317 QueryResult result = xpathQuery.execute();
318
319 NodeIterator nit = result.getNodes();
320 viewer.setInput(JcrUtils.nodeIteratorToList(nit));
321 // if (log.isTraceEnabled()) {
322 // long end = System.currentTimeMillis();
323 // log.trace("Quick Search - Found: " + nit.getSize() + " in " + (end - begin)
324 // + " ms by executing XPath query (" + xpathQueryStr + ").");
325 // }
326 } catch (RepositoryException e) {
327 throw new IllegalStateException("Unable to list entities", e);
328 }
329 }
330
331 public TableViewer getViewer() {
332 return viewer;
333 }
334
335 class SingleEntityLabelProvider extends ColumnLabelProvider {
336 private static final long serialVersionUID = -2209337675781795677L;
337
338 @Override
339 public String getText(Object element) {
340 return Jcr.getTitle((Node) element);
341 }
342
343 }
344
345 class BasicNodeListContentProvider implements IStructuredContentProvider {
346 private static final long serialVersionUID = 1L;
347 // keep a cache of the Nodes in the content provider to be able to
348 // manage long request
349 private List<Node> nodes;
350
351 public void dispose() {
352 }
353
354 /** Expects a list of nodes as a new input */
355 @SuppressWarnings("unchecked")
356 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
357 nodes = (List<Node>) newInput;
358 }
359
360 public Object[] getElements(Object arg0) {
361 return nodes.toArray();
362 }
363 }
364 }
365 }