1 package org
.argeo
.cms
.ui
.viewers
;
3 import java
.security
.AccessControlContext
;
4 import java
.security
.AccessController
;
5 import java
.security
.PrivilegedAction
;
6 import java
.util
.Observable
;
7 import java
.util
.Observer
;
10 import javax
.jcr
.RepositoryException
;
11 import javax
.jcr
.Session
;
12 import javax
.security
.auth
.Subject
;
14 import org
.argeo
.api
.cms
.CmsLog
;
15 import org
.argeo
.api
.cms
.ux
.CmsEditable
;
16 import org
.argeo
.cms
.swt
.SwtEditablePart
;
17 import org
.argeo
.cms
.swt
.widgets
.ScrolledPage
;
18 import org
.argeo
.jcr
.JcrException
;
19 import org
.eclipse
.jface
.viewers
.ContentViewer
;
20 import org
.eclipse
.jface
.viewers
.ISelection
;
21 import org
.eclipse
.jface
.viewers
.StructuredSelection
;
22 import org
.eclipse
.swt
.SWT
;
23 import org
.eclipse
.swt
.events
.FocusEvent
;
24 import org
.eclipse
.swt
.events
.FocusListener
;
25 import org
.eclipse
.swt
.events
.MouseAdapter
;
26 import org
.eclipse
.swt
.events
.MouseListener
;
27 import org
.eclipse
.swt
.widgets
.Composite
;
28 import org
.eclipse
.swt
.widgets
.Control
;
29 import org
.eclipse
.swt
.widgets
.Widget
;
30 import org
.xml
.sax
.SAXParseException
;
32 /** Base class for viewers related to a page */
33 public abstract class AbstractPageViewer
extends ContentViewer
implements Observer
{
34 private static final long serialVersionUID
= 5438688173410341485L;
36 private final static CmsLog log
= CmsLog
.getLog(AbstractPageViewer
.class);
38 private final boolean readOnly
;
39 /** The basis for the layouts, typically a ScrolledPage. */
40 private final Composite page
;
41 private final CmsEditable cmsEditable
;
43 private MouseListener mouseListener
;
44 private FocusListener focusListener
;
46 private SwtEditablePart edited
;
47 private ISelection selection
= StructuredSelection
.EMPTY
;
49 private AccessControlContext accessControlContext
;
51 protected AbstractPageViewer(Section parent
, int style
, CmsEditable cmsEditable
) {
52 // read only at UI level
53 readOnly
= SWT
.READ_ONLY
== (style
& SWT
.READ_ONLY
);
55 this.cmsEditable
= cmsEditable
== null ? CmsEditable
.NON_EDITABLE
: cmsEditable
;
56 if (this.cmsEditable
instanceof Observable
)
57 ((Observable
) this.cmsEditable
).addObserver(this);
59 if (cmsEditable
.canEdit()) {
60 mouseListener
= createMouseListener();
61 focusListener
= createFocusListener();
63 page
= findPage(parent
);
64 accessControlContext
= AccessController
.getContext();
68 * Can be called to simplify the called to isModelInitialized() and initModel()
70 protected void initModelIfNeeded(Node node
) {
72 if (!isModelInitialized(node
))
73 if (getCmsEditable().canEdit()) {
75 node
.getSession().save();
77 } catch (RepositoryException e
) {
78 throw new JcrException("Cannot initialize model", e
);
82 /** Called if user can edit and model is not initialized */
83 protected Boolean
isModelInitialized(Node node
) throws RepositoryException
{
87 /** Called if user can edit and model is not initialized */
88 protected void initModel(Node node
) throws RepositoryException
{
91 /** Create (retrieve) the MouseListener to use. */
92 protected MouseListener
createMouseListener() {
93 return new MouseAdapter() {
94 private static final long serialVersionUID
= 1L;
98 /** Create (retrieve) the FocusListener to use. */
99 protected FocusListener
createFocusListener() {
100 return new FocusListener() {
101 private static final long serialVersionUID
= 1L;
104 public void focusLost(FocusEvent event
) {
108 public void focusGained(FocusEvent event
) {
113 protected Composite
findPage(Composite composite
) {
114 if (composite
instanceof ScrolledPage
) {
115 return (ScrolledPage
) composite
;
117 if (composite
.getParent() == null)
119 return findPage(composite
.getParent());
123 public void layoutPage() {
125 page
.layout(true, true);
128 protected void showControl(Control control
) {
129 if (page
!= null && (page
instanceof ScrolledPage
))
130 ((ScrolledPage
) page
).showControl(control
);
134 public void update(Observable o
, Object arg
) {
135 if (o
== cmsEditable
)
136 editingStateChanged(cmsEditable
);
139 /** To be overridden in order to provide the actual refresh */
140 protected void refresh(Control control
) throws RepositoryException
{
143 /** To be overridden.Save the edited part. */
144 protected void save(SwtEditablePart part
) throws RepositoryException
{
147 /** Prepare the edited part */
148 protected void prepare(SwtEditablePart part
, Object caretPosition
) {
151 /** Notified when the editing state changed. Does nothing, to be overridden */
152 protected void editingStateChanged(CmsEditable cmsEditable
) {
156 public void refresh() {
157 // TODO check actual context in order to notice a discrepancy
158 Subject viewerSubject
= getViewerSubject();
159 Subject
.doAs(viewerSubject
, (PrivilegedAction
<Void
>) () -> {
161 if (cmsEditable
.canEdit() && !readOnly
)
162 mouseListener
= createMouseListener();
164 mouseListener
= null;
165 refresh(getControl());
166 // layout(getControl());
167 if (!getControl().isDisposed())
169 } catch (RepositoryException e
) {
170 throw new JcrException("Cannot refresh", e
);
177 public void setSelection(ISelection selection
, boolean reveal
) {
178 this.selection
= selection
;
181 protected void updateContent(SwtEditablePart part
) throws RepositoryException
{
185 protected void edit(SwtEditablePart part
, Object caretPosition
) {
190 if (edited
!= null && edited
!= part
) {
191 SwtEditablePart previouslyEdited
= edited
;
194 } catch (Exception e
) {
195 notifyEditionException(e
);
196 edit(previouslyEdited
, caretPosition
);
204 prepare(part
, caretPosition
);
205 edited
.getControl().addFocusListener(new FocusListener() {
206 private static final long serialVersionUID
= 6883521812717097017L;
209 public void focusLost(FocusEvent event
) {
214 public void focusGained(FocusEvent event
) {
218 layout(part
.getControl());
219 showControl(part
.getControl());
220 } catch (RepositoryException e
) {
221 throw new JcrException("Cannot edit " + part
, e
);
225 protected void stopEditing(Boolean save
) {
226 if (edited
instanceof Widget
&& ((Widget
) edited
).isDisposed()) {
231 assert edited
!= null;
232 if (edited
== null) {
233 if (log
.isTraceEnabled())
234 log
.warn("Told to stop editing while not editing anything");
242 edited
.stopEditing();
243 SwtEditablePart editablePart
= edited
;
244 Control control
= ((SwtEditablePart
) edited
).getControl();
246 // TODO make edited state management more robust
247 updateContent(editablePart
);
249 } catch (RepositoryException e
) {
250 throw new JcrException("Cannot stop editing", e
);
256 // METHODS AVAILABLE TO EXTENDING CLASSES
257 protected void saveEdit() {
262 protected void cancelEdit() {
267 /** Layout this controls from the related base page. */
268 public void layout(Control
... controls
) {
269 page
.layout(controls
);
273 * Find the first {@link SwtEditablePart} in the parents hierarchy of this control
275 protected SwtEditablePart
findDataParent(Control parent
) {
276 if (parent
instanceof SwtEditablePart
) {
277 return (SwtEditablePart
) parent
;
279 if (parent
.getParent() != null)
280 return findDataParent(parent
.getParent());
282 throw new IllegalStateException("No data parent found");
286 /** Check whether the edited part is in a proper state */
287 protected void checkEdited() {
288 if (edited
== null || (edited
instanceof Widget
) && ((Widget
) edited
).isDisposed())
289 throw new IllegalStateException("Edited should not be null or disposed at this stage");
292 /** Persist all changes. */
293 protected void persistChanges(Session session
) throws RepositoryException
{
295 session
.refresh(false);
296 // TODO notify that changes have been persisted
299 /** Convenience method using a Node in order to save the underlying session. */
300 protected void persistChanges(Node anyNode
) throws RepositoryException
{
301 persistChanges(anyNode
.getSession());
304 /** Notify edition exception */
305 protected void notifyEditionException(Throwable e
) {
306 Throwable eToLog
= e
;
307 if (e
instanceof IllegalArgumentException
)
308 if (e
.getCause() instanceof SAXParseException
)
309 eToLog
= e
.getCause();
310 log
.error(eToLog
.getMessage(), eToLog
);
311 // if (log.isTraceEnabled())
312 // log.trace("Full stack of " + eToLog.getMessage(), e);
313 // TODO Light error notification popup
316 protected Subject
getViewerSubject() {
318 if (accessControlContext
!= null) {
319 res
= Subject
.getSubject(accessControlContext
);
322 throw new IllegalStateException("No subject associated with this viewer");
327 public boolean isReadOnly() {
331 protected SwtEditablePart
getEdited() {
335 public MouseListener
getMouseListener() {
336 return mouseListener
;
339 public FocusListener
getFocusListener() {
340 return focusListener
;
343 public CmsEditable
getCmsEditable() {
348 public ISelection
getSelection() {