1 package org
.argeo
.cms
.e4
.maintenance
;
3 import static org
.eclipse
.swt
.SWT
.RIGHT
;
5 import java
.text
.DateFormat
;
6 import java
.text
.SimpleDateFormat
;
7 import java
.util
.LinkedHashMap
;
9 import javax
.jcr
.ItemNotFoundException
;
10 import javax
.jcr
.Node
;
11 import javax
.jcr
.NodeIterator
;
12 import javax
.jcr
.Property
;
13 import javax
.jcr
.PropertyIterator
;
14 import javax
.jcr
.PropertyType
;
15 import javax
.jcr
.RepositoryException
;
16 import javax
.jcr
.Value
;
18 import org
.argeo
.cms
.CmsException
;
19 import org
.argeo
.cms
.ui
.CmsUiProvider
;
20 import org
.argeo
.cms
.ui
.util
.CmsLink
;
21 import org
.argeo
.cms
.ui
.util
.CmsUiUtils
;
22 import org
.argeo
.cms
.ui
.widgets
.EditableImage
;
23 import org
.argeo
.cms
.ui
.widgets
.Img
;
24 import org
.argeo
.jcr
.JcrUtils
;
25 import org
.eclipse
.jface
.viewers
.ColumnLabelProvider
;
26 import org
.eclipse
.jface
.viewers
.ILazyContentProvider
;
27 import org
.eclipse
.jface
.viewers
.ISelectionChangedListener
;
28 import org
.eclipse
.jface
.viewers
.IStructuredSelection
;
29 import org
.eclipse
.jface
.viewers
.SelectionChangedEvent
;
30 import org
.eclipse
.jface
.viewers
.StructuredSelection
;
31 import org
.eclipse
.jface
.viewers
.TableViewer
;
32 import org
.eclipse
.jface
.viewers
.TableViewerColumn
;
33 import org
.eclipse
.jface
.viewers
.Viewer
;
34 import org
.eclipse
.swt
.SWT
;
35 import org
.eclipse
.swt
.custom
.ScrolledComposite
;
36 import org
.eclipse
.swt
.events
.ControlAdapter
;
37 import org
.eclipse
.swt
.events
.ControlEvent
;
38 import org
.eclipse
.swt
.events
.KeyEvent
;
39 import org
.eclipse
.swt
.events
.KeyListener
;
40 import org
.eclipse
.swt
.events
.ModifyEvent
;
41 import org
.eclipse
.swt
.events
.ModifyListener
;
42 import org
.eclipse
.swt
.graphics
.Point
;
43 import org
.eclipse
.swt
.graphics
.Rectangle
;
44 import org
.eclipse
.swt
.layout
.GridData
;
45 import org
.eclipse
.swt
.layout
.GridLayout
;
46 import org
.eclipse
.swt
.widgets
.Composite
;
47 import org
.eclipse
.swt
.widgets
.Control
;
48 import org
.eclipse
.swt
.widgets
.Label
;
49 import org
.eclipse
.swt
.widgets
.Table
;
50 import org
.eclipse
.swt
.widgets
.TableColumn
;
51 import org
.eclipse
.swt
.widgets
.Text
;
53 public class Browse
implements CmsUiProvider
{
55 // Some local constants to experiment. should be cleaned
56 private final static String BROWSE_PREFIX
= "browse#";
57 private final static int THUMBNAIL_WIDTH
= 400;
58 private final static int COLUMN_WIDTH
= 160;
59 private DateFormat timeFormatter
= new SimpleDateFormat("dd-MM-yyyy', 'HH:mm");
61 // keep a cache of the opened nodes
63 private LinkedHashMap
<String
, FilterEntitiesVirtualTable
> browserCols
= new LinkedHashMap
<String
, Browse
.FilterEntitiesVirtualTable
>();
64 private Composite nodeDisplayParent
;
65 private Composite colViewer
;
66 private ScrolledComposite scrolledCmp
;
67 private Text parentPathTxt
;
68 private Text filterTxt
;
69 private Node currEdited
;
71 private String initialPath
;
74 public Control
createUi(Composite parent
, Node context
) throws RepositoryException
{
77 throw new CmsException("Context cannot be null");
78 GridLayout layout
= CmsUiUtils
.noSpaceGridLayout();
79 layout
.numColumns
= 2;
80 parent
.setLayout(layout
);
83 Composite leftCmp
= new Composite(parent
, SWT
.NO_FOCUS
);
84 leftCmp
.setLayoutData(CmsUiUtils
.fillAll());
85 createBrowserPart(leftCmp
, context
);
88 nodeDisplayParent
= new Composite(parent
, SWT
.NO_FOCUS
| SWT
.BORDER
);
89 GridData gd
= new GridData(SWT
.RIGHT
, SWT
.FILL
, false, true);
90 gd
.widthHint
= THUMBNAIL_WIDTH
;
91 nodeDisplayParent
.setLayoutData(gd
);
92 createNodeView(nodeDisplayParent
, context
);
96 initialPath
= context
.getPath();
98 // Workaround we don't yet manage the delete to display parent of the
99 // initial context node
104 private void createBrowserPart(Composite parent
, Node context
) throws RepositoryException
{
105 GridLayout layout
= CmsUiUtils
.noSpaceGridLayout();
106 parent
.setLayout(layout
);
107 Composite filterCmp
= new Composite(parent
, SWT
.NO_FOCUS
);
108 filterCmp
.setLayoutData(CmsUiUtils
.fillWidth());
111 addFilterPanel(filterCmp
);
113 // scrolled composite
114 scrolledCmp
= new ScrolledComposite(parent
, SWT
.H_SCROLL
| SWT
.BORDER
| SWT
.NO_FOCUS
);
115 scrolledCmp
.setLayoutData(CmsUiUtils
.fillAll());
116 scrolledCmp
.setExpandVertical(true);
117 scrolledCmp
.setExpandHorizontal(true);
118 scrolledCmp
.setShowFocusedControl(true);
120 colViewer
= new Composite(scrolledCmp
, SWT
.NO_FOCUS
);
121 scrolledCmp
.setContent(colViewer
);
122 scrolledCmp
.addControlListener(new ControlAdapter() {
123 private static final long serialVersionUID
= 6589392045145698201L;
126 public void controlResized(ControlEvent e
) {
127 Rectangle r
= scrolledCmp
.getClientArea();
128 scrolledCmp
.setMinSize(colViewer
.computeSize(SWT
.DEFAULT
, r
.height
));
131 initExplorer(colViewer
, context
);
134 private Control
initExplorer(Composite parent
, Node context
) throws RepositoryException
{
135 parent
.setLayout(CmsUiUtils
.noSpaceGridLayout());
136 createBrowserColumn(parent
, context
);
140 private Control
createBrowserColumn(Composite parent
, Node context
) throws RepositoryException
{
141 // TODO style is not correctly managed.
142 FilterEntitiesVirtualTable table
= new FilterEntitiesVirtualTable(parent
, SWT
.BORDER
| SWT
.NO_FOCUS
, context
);
143 // CmsUiUtils.style(table, ArgeoOrgStyle.browserColumn.style());
144 table
.filterList("*");
145 table
.setLayoutData(new GridData(SWT
.LEFT
, SWT
.FILL
, false, true));
146 browserCols
.put(context
.getPath(), table
);
150 public void addFilterPanel(Composite parent
) {
152 parent
.setLayout(CmsUiUtils
.noSpaceGridLayout(new GridLayout(2, false)));
154 // Text Area for the filter
155 parentPathTxt
= new Text(parent
, SWT
.NO_FOCUS
);
156 parentPathTxt
.setEditable(false);
157 filterTxt
= new Text(parent
, SWT
.SEARCH
| SWT
.ICON_CANCEL
);
158 filterTxt
.setMessage("Filter current list");
159 filterTxt
.setLayoutData(CmsUiUtils
.fillWidth());
160 filterTxt
.addModifyListener(new ModifyListener() {
161 private static final long serialVersionUID
= 7709303319740056286L;
163 public void modifyText(ModifyEvent event
) {
168 filterTxt
.addKeyListener(new KeyListener() {
169 private static final long serialVersionUID
= -4523394262771183968L;
172 public void keyReleased(KeyEvent e
) {
176 public void keyPressed(KeyEvent e
) {
177 boolean shiftPressed
= (e
.stateMask
& SWT
.SHIFT
) != 0;
178 // boolean altPressed = (e.stateMask & SWT.ALT) != 0;
179 FilterEntitiesVirtualTable currTable
= null;
180 if (currEdited
!= null) {
181 FilterEntitiesVirtualTable table
= browserCols
.get(getPath(currEdited
));
182 if (table
!= null && !table
.isDisposed())
187 if (e
.keyCode
== SWT
.ARROW_DOWN
)
188 currTable
.setFocus();
189 else if (e
.keyCode
== SWT
.BS
) {
190 if (filterTxt
.getText().equals("")
191 && !(getPath(currEdited
).equals("/") || getPath(currEdited
).equals(initialPath
))) {
192 setEdited(currEdited
.getParent());
194 filterTxt
.setFocus();
196 } else if (e
.keyCode
== SWT
.TAB
&& !shiftPressed
) {
197 if (currEdited
.getNodes(filterTxt
.getText() + "*").getSize() == 1) {
198 setEdited(currEdited
.getNodes(filterTxt
.getText() + "*").nextNode());
200 filterTxt
.setFocus();
203 } catch (RepositoryException e1
) {
204 throw new CmsException("Unexpected error in key management for " + currEdited
+ "with filter "
205 + filterTxt
.getText(), e1
);
212 private void setEdited(Node node
) {
215 CmsUiUtils
.clear(nodeDisplayParent
);
216 createNodeView(nodeDisplayParent
, currEdited
);
217 nodeDisplayParent
.layout();
218 refreshFilters(node
);
219 refreshBrowser(node
);
220 } catch (RepositoryException re
) {
221 throw new CmsException("Unable to update browser for " + node
, re
);
225 private void refreshFilters(Node node
) throws RepositoryException
{
226 String currNodePath
= node
.getPath();
227 parentPathTxt
.setText(currNodePath
);
228 filterTxt
.setText("");
229 filterTxt
.getParent().layout();
232 private void refreshBrowser(Node node
) throws RepositoryException
{
235 String currNodePath
= node
.getPath();
236 String currParPath
= "";
237 if (!"/".equals(currNodePath
))
238 currParPath
= JcrUtils
.parentPath(currNodePath
);
239 if ("".equals(currParPath
))
242 Object
[][] colMatrix
= new Object
[browserCols
.size()][2];
244 int i
= 0, j
= -1, k
= -1;
245 for (String path
: browserCols
.keySet()) {
246 colMatrix
[i
][0] = path
;
247 colMatrix
[i
][1] = browserCols
.get(path
);
248 if (j
>= 0 && k
< 0 && !currNodePath
.equals("/")) {
249 boolean leaveOpened
= path
.startsWith(currNodePath
);
251 // workaround for same name siblings
252 // fix me weird side effect when we go left or click on anb
253 // already selected, unfocused node
254 if (leaveOpened
&& (path
.lastIndexOf("/") == 0 && currNodePath
.lastIndexOf("/") == 0
255 || JcrUtils
.parentPath(path
).equals(JcrUtils
.parentPath(currNodePath
))))
256 leaveOpened
= JcrUtils
.lastPathElement(path
).equals(JcrUtils
.lastPathElement(currNodePath
));
261 if (currParPath
.equals(path
))
266 if (j
>= 0 && k
>= 0)
267 // remove useless cols
268 for (int l
= i
- 1; l
>= k
; l
--) {
269 browserCols
.remove(colMatrix
[l
][0]);
270 ((FilterEntitiesVirtualTable
) colMatrix
[l
][1]).dispose();
273 // Remove disposed columns
274 // TODO investigate and fix the mechanism that leave them there after
276 if (browserCols
.containsKey(currNodePath
)) {
277 FilterEntitiesVirtualTable currCol
= browserCols
.get(currNodePath
);
278 if (currCol
.isDisposed())
279 browserCols
.remove(currNodePath
);
282 if (!browserCols
.containsKey(currNodePath
))
283 createBrowserColumn(colViewer
, node
);
285 colViewer
.setLayout(CmsUiUtils
.noSpaceGridLayout(new GridLayout(browserCols
.size(), false)));
288 // also resize the scrolled composite
289 scrolledCmp
.layout();
290 scrolledCmp
.getShowFocusedControl();
291 // colViewer.getParent().layout();
292 // if (JcrUtils.parentPath(currNodePath).equals(currBrowserKey)) {
297 private void modifyFilter(boolean fromOutside
) {
299 if (currEdited
!= null) {
300 String filter
= filterTxt
.getText() + "*";
301 FilterEntitiesVirtualTable table
= browserCols
.get(getPath(currEdited
));
302 if (table
!= null && !table
.isDisposed())
303 table
.filterList(filter
);
308 private String
getPath(Node node
) {
310 return node
.getPath();
311 } catch (RepositoryException e
) {
312 throw new CmsException("Unable to get path for node " + node
, e
);
316 private Point imageWidth
= new Point(250, 0);
319 * Recreates the content of the box that displays information about the current
322 private Control
createNodeView(Composite parent
, Node context
) throws RepositoryException
{
324 parent
.setLayout(new GridLayout(2, false));
326 if (isImg(context
)) {
327 EditableImage image
= new Img(parent
, RIGHT
, context
, imageWidth
);
328 image
.setLayoutData(new GridData(SWT
.CENTER
, SWT
.CENTER
, true, false, 2, 1));
331 // Name and primary type
332 Label contextL
= new Label(parent
, SWT
.NONE
);
333 CmsUiUtils
.markup(contextL
);
334 contextL
.setText("<b>" + context
.getName() + "</b>");
335 new Label(parent
, SWT
.NONE
).setText(context
.getPrimaryNodeType().getName());
338 for (NodeIterator nIt
= context
.getNodes(); nIt
.hasNext();) {
339 Node child
= nIt
.nextNode();
340 new CmsLink(child
.getName(), BROWSE_PREFIX
+ child
.getPath()).createUi(parent
, context
);
341 new Label(parent
, SWT
.NONE
).setText(child
.getPrimaryNodeType().getName());
345 for (PropertyIterator pIt
= context
.getProperties(); pIt
.hasNext();) {
346 Property property
= pIt
.nextProperty();
347 Label label
= new Label(parent
, SWT
.NONE
);
348 label
.setText(property
.getName());
349 label
.setToolTipText(JcrUtils
.getPropertyDefinitionAsString(property
));
350 new Label(parent
, SWT
.NONE
).setText(getPropAsString(property
));
356 private boolean isImg(Node node
) throws RepositoryException
{
357 // TODO support images
359 // return node.hasNode(JCR_CONTENT) && node.isNodeType(CmsTypes.CMS_IMAGE);
362 private String
getPropAsString(Property property
) throws RepositoryException
{
364 if (property
.isMultiple()) {
365 result
= getMultiAsString(property
, ", ");
367 Value value
= property
.getValue();
368 if (value
.getType() == PropertyType
.BINARY
)
370 else if (value
.getType() == PropertyType
.DATE
)
371 result
= timeFormatter
.format(value
.getDate().getTime());
373 result
= value
.getString();
378 private String
getMultiAsString(Property property
, String separator
) throws RepositoryException
{
379 if (separator
== null)
381 Value
[] values
= property
.getValues();
382 StringBuilder builder
= new StringBuilder();
383 for (Value val
: values
) {
384 String currStr
= val
.getString();
385 if (!"".equals(currStr
.trim()))
386 builder
.append(currStr
).append(separator
);
388 if (builder
.lastIndexOf(separator
) >= 0)
389 return builder
.substring(0, builder
.length() - separator
.length());
391 return builder
.toString();
394 /** Almost canonical implementation of a table that display entities */
395 private class FilterEntitiesVirtualTable
extends Composite
{
396 private static final long serialVersionUID
= 8798147431706283824L;
399 private Node context
;
402 private TableViewer entityViewer
;
404 // enable management of multiple columns
410 public boolean setFocus() {
411 if (entityViewer
.getTable().isDisposed())
413 if (entityViewer
.getSelection().isEmpty()) {
414 Object first
= entityViewer
.getElementAt(0);
416 entityViewer
.setSelection(new StructuredSelection(first
), true);
419 return entityViewer
.getTable().setFocus();
422 void filterList(String filter
) {
424 NodeIterator nit
= context
.getNodes(filter
);
425 refreshFilteredList(nit
);
426 } catch (RepositoryException e
) {
427 throw new CmsException("Unable to filter " + getNode() + " children with filter " + filter
, e
);
432 public FilterEntitiesVirtualTable(Composite parent
, int style
, Node context
) {
433 super(parent
, SWT
.NO_FOCUS
);
434 this.context
= context
;
438 protected void populate() {
439 Composite parent
= this;
440 GridLayout layout
= CmsUiUtils
.noSpaceGridLayout();
442 this.setLayout(layout
);
443 createTableViewer(parent
);
446 private void createTableViewer(final Composite parent
) {
448 // We must limit the size of the table otherwise the full list is
450 // before the layout happens
451 Composite listCmp
= new Composite(parent
, SWT
.NO_FOCUS
);
452 GridData gd
= new GridData(SWT
.LEFT
, SWT
.FILL
, false, true);
453 gd
.widthHint
= COLUMN_WIDTH
;
454 listCmp
.setLayoutData(gd
);
455 listCmp
.setLayout(CmsUiUtils
.noSpaceGridLayout());
457 entityViewer
= new TableViewer(listCmp
, SWT
.VIRTUAL
| SWT
.SINGLE
);
458 Table table
= entityViewer
.getTable();
460 table
.setLayoutData(CmsUiUtils
.fillAll());
461 table
.setLinesVisible(true);
462 table
.setHeaderVisible(false);
463 CmsUiUtils
.markup(table
);
465 CmsUiUtils
.style(table
, MaintenanceStyles
.BROWSER_COLUMN
);
468 TableViewerColumn column
= new TableViewerColumn(entityViewer
, SWT
.NONE
);
469 TableColumn tcol
= column
.getColumn();
470 tcol
.setWidth(COLUMN_WIDTH
);
471 tcol
.setResizable(true);
472 column
.setLabelProvider(new SimpleNameLP());
474 entityViewer
.setContentProvider(new MyLazyCP(entityViewer
));
475 entityViewer
.addSelectionChangedListener(new ISelectionChangedListener() {
478 public void selectionChanged(SelectionChangedEvent event
) {
479 IStructuredSelection selection
= (IStructuredSelection
) entityViewer
.getSelection();
480 if (selection
.isEmpty())
483 setEdited((Node
) selection
.getFirstElement());
488 table
.addKeyListener(new KeyListener() {
489 private static final long serialVersionUID
= -330694313896036230L;
492 public void keyReleased(KeyEvent e
) {
496 public void keyPressed(KeyEvent e
) {
498 IStructuredSelection selection
= (IStructuredSelection
) entityViewer
.getSelection();
499 Node selected
= null;
500 if (!selection
.isEmpty())
501 selected
= ((Node
) selection
.getFirstElement());
503 if (e
.keyCode
== SWT
.ARROW_RIGHT
) {
504 if (selected
!= null) {
506 browserCols
.get(selected
.getPath()).setFocus();
508 } else if (e
.keyCode
== SWT
.ARROW_LEFT
) {
510 selected
= getNode().getParent();
511 String newPath
= selected
.getPath(); // getNode().getParent()
513 if (browserCols
.containsKey(newPath
))
514 browserCols
.get(newPath
).setFocus();
515 } catch (ItemNotFoundException ie
) {
519 } catch (RepositoryException ie
) {
520 throw new CmsException("Error while managing arrow " + "events in the browser for " + selected
,
527 private class MyLazyCP
implements ILazyContentProvider
{
528 private static final long serialVersionUID
= 1L;
529 private TableViewer viewer
;
530 private Object
[] elements
;
532 public MyLazyCP(TableViewer viewer
) {
533 this.viewer
= viewer
;
536 public void dispose() {
539 public void inputChanged(Viewer viewer
, Object oldInput
, Object newInput
) {
540 // IMPORTANT: don't forget this: an exception will be thrown if
541 // a selected object is not part of the results anymore.
542 viewer
.setSelection(null);
543 this.elements
= (Object
[]) newInput
;
546 public void updateElement(int index
) {
547 viewer
.replace(elements
[index
], index
);
551 protected void refreshFilteredList(NodeIterator children
) {
552 Object
[] rows
= JcrUtils
.nodeIteratorToList(children
).toArray();
553 entityViewer
.setInput(rows
);
554 entityViewer
.setItemCount(rows
.length
);
555 entityViewer
.refresh();
558 public class SimpleNameLP
extends ColumnLabelProvider
{
559 private static final long serialVersionUID
= 2465059387875338553L;
562 public String
getText(Object element
) {
563 if (element
instanceof Node
) {
564 Node curr
= ((Node
) element
);
566 return curr
.getName();
567 } catch (RepositoryException e
) {
568 throw new CmsException("Unable to get name for" + curr
);
571 return super.getText(element
);