]> git.argeo.org Git - gpl/argeo-jcr.git/blob - swt/org.argeo.cms.jcr.ui/src/org/argeo/cms/ui/viewers/AbstractPageViewer.java
Adapt to changes in Argeo Commons
[gpl/argeo-jcr.git] / swt / org.argeo.cms.jcr.ui / src / org / argeo / cms / ui / viewers / AbstractPageViewer.java
1 package org.argeo.cms.ui.viewers;
2
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;
8
9 import javax.jcr.Node;
10 import javax.jcr.RepositoryException;
11 import javax.jcr.Session;
12 import javax.security.auth.Subject;
13
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;
31
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;
35
36 private final static CmsLog log = CmsLog.getLog(AbstractPageViewer.class);
37
38 private final boolean readOnly;
39 /** The basis for the layouts, typically a ScrolledPage. */
40 private final Composite page;
41 private final CmsEditable cmsEditable;
42
43 private MouseListener mouseListener;
44 private FocusListener focusListener;
45
46 private SwtEditablePart edited;
47 private ISelection selection = StructuredSelection.EMPTY;
48
49 private AccessControlContext accessControlContext;
50
51 protected AbstractPageViewer(Section parent, int style, CmsEditable cmsEditable) {
52 // read only at UI level
53 readOnly = SWT.READ_ONLY == (style & SWT.READ_ONLY);
54
55 this.cmsEditable = cmsEditable == null ? CmsEditable.NON_EDITABLE : cmsEditable;
56 if (this.cmsEditable instanceof Observable)
57 ((Observable) this.cmsEditable).addObserver(this);
58
59 if (cmsEditable.canEdit()) {
60 mouseListener = createMouseListener();
61 focusListener = createFocusListener();
62 }
63 page = findPage(parent);
64 accessControlContext = AccessController.getContext();
65 }
66
67 /**
68 * Can be called to simplify the called to isModelInitialized() and initModel()
69 */
70 protected void initModelIfNeeded(Node node) {
71 try {
72 if (!isModelInitialized(node))
73 if (getCmsEditable().canEdit()) {
74 initModel(node);
75 node.getSession().save();
76 }
77 } catch (RepositoryException e) {
78 throw new JcrException("Cannot initialize model", e);
79 }
80 }
81
82 /** Called if user can edit and model is not initialized */
83 protected Boolean isModelInitialized(Node node) throws RepositoryException {
84 return true;
85 }
86
87 /** Called if user can edit and model is not initialized */
88 protected void initModel(Node node) throws RepositoryException {
89 }
90
91 /** Create (retrieve) the MouseListener to use. */
92 protected MouseListener createMouseListener() {
93 return new MouseAdapter() {
94 private static final long serialVersionUID = 1L;
95 };
96 }
97
98 /** Create (retrieve) the FocusListener to use. */
99 protected FocusListener createFocusListener() {
100 return new FocusListener() {
101 private static final long serialVersionUID = 1L;
102
103 @Override
104 public void focusLost(FocusEvent event) {
105 }
106
107 @Override
108 public void focusGained(FocusEvent event) {
109 }
110 };
111 }
112
113 protected Composite findPage(Composite composite) {
114 if (composite instanceof ScrolledPage) {
115 return (ScrolledPage) composite;
116 } else {
117 if (composite.getParent() == null)
118 return composite;
119 return findPage(composite.getParent());
120 }
121 }
122
123 public void layoutPage() {
124 if (page != null)
125 page.layout(true, true);
126 }
127
128 protected void showControl(Control control) {
129 if (page != null && (page instanceof ScrolledPage))
130 ((ScrolledPage) page).showControl(control);
131 }
132
133 @Override
134 public void update(Observable o, Object arg) {
135 if (o == cmsEditable)
136 editingStateChanged(cmsEditable);
137 }
138
139 /** To be overridden in order to provide the actual refresh */
140 protected void refresh(Control control) throws RepositoryException {
141 }
142
143 /** To be overridden.Save the edited part. */
144 protected void save(SwtEditablePart part) throws RepositoryException {
145 }
146
147 /** Prepare the edited part */
148 protected void prepare(SwtEditablePart part, Object caretPosition) {
149 }
150
151 /** Notified when the editing state changed. Does nothing, to be overridden */
152 protected void editingStateChanged(CmsEditable cmsEditable) {
153 }
154
155 @Override
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>) () -> {
160 try {
161 if (cmsEditable.canEdit() && !readOnly)
162 mouseListener = createMouseListener();
163 else
164 mouseListener = null;
165 refresh(getControl());
166 // layout(getControl());
167 if (!getControl().isDisposed())
168 layoutPage();
169 } catch (RepositoryException e) {
170 throw new JcrException("Cannot refresh", e);
171 }
172 return null;
173 });
174 }
175
176 @Override
177 public void setSelection(ISelection selection, boolean reveal) {
178 this.selection = selection;
179 }
180
181 protected void updateContent(SwtEditablePart part) throws RepositoryException {
182 }
183
184 // LOW LEVEL EDITION
185 protected void edit(SwtEditablePart part, Object caretPosition) {
186 try {
187 if (edited == part)
188 return;
189
190 if (edited != null && edited != part) {
191 SwtEditablePart previouslyEdited = edited;
192 try {
193 stopEditing(true);
194 } catch (Exception e) {
195 notifyEditionException(e);
196 edit(previouslyEdited, caretPosition);
197 return;
198 }
199 }
200
201 part.startEditing();
202 edited = part;
203 updateContent(part);
204 prepare(part, caretPosition);
205 edited.getControl().addFocusListener(new FocusListener() {
206 private static final long serialVersionUID = 6883521812717097017L;
207
208 @Override
209 public void focusLost(FocusEvent event) {
210 stopEditing(true);
211 }
212
213 @Override
214 public void focusGained(FocusEvent event) {
215 }
216 });
217
218 layout(part.getControl());
219 showControl(part.getControl());
220 } catch (RepositoryException e) {
221 throw new JcrException("Cannot edit " + part, e);
222 }
223 }
224
225 protected void stopEditing(Boolean save) {
226 if (edited instanceof Widget && ((Widget) edited).isDisposed()) {
227 edited = null;
228 return;
229 }
230
231 assert edited != null;
232 if (edited == null) {
233 if (log.isTraceEnabled())
234 log.warn("Told to stop editing while not editing anything");
235 return;
236 }
237
238 try {
239 if (save)
240 save(edited);
241
242 edited.stopEditing();
243 SwtEditablePart editablePart = edited;
244 Control control = ((SwtEditablePart) edited).getControl();
245 edited = null;
246 // TODO make edited state management more robust
247 updateContent(editablePart);
248 layout(control);
249 } catch (RepositoryException e) {
250 throw new JcrException("Cannot stop editing", e);
251 } finally {
252 edited = null;
253 }
254 }
255
256 // METHODS AVAILABLE TO EXTENDING CLASSES
257 protected void saveEdit() {
258 if (edited != null)
259 stopEditing(true);
260 }
261
262 protected void cancelEdit() {
263 if (edited != null)
264 stopEditing(false);
265 }
266
267 /** Layout this controls from the related base page. */
268 public void layout(Control... controls) {
269 page.layout(controls);
270 }
271
272 /**
273 * Find the first {@link SwtEditablePart} in the parents hierarchy of this control
274 */
275 protected SwtEditablePart findDataParent(Control parent) {
276 if (parent instanceof SwtEditablePart) {
277 return (SwtEditablePart) parent;
278 }
279 if (parent.getParent() != null)
280 return findDataParent(parent.getParent());
281 else
282 throw new IllegalStateException("No data parent found");
283 }
284
285 // UTILITIES
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");
290 }
291
292 /** Persist all changes. */
293 protected void persistChanges(Session session) throws RepositoryException {
294 session.save();
295 session.refresh(false);
296 // TODO notify that changes have been persisted
297 }
298
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());
302 }
303
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
314 }
315
316 protected Subject getViewerSubject() {
317 Subject res = null;
318 if (accessControlContext != null) {
319 res = Subject.getSubject(accessControlContext);
320 }
321 if (res == null)
322 throw new IllegalStateException("No subject associated with this viewer");
323 return res;
324 }
325
326 // GETTERS / SETTERS
327 public boolean isReadOnly() {
328 return readOnly;
329 }
330
331 protected SwtEditablePart getEdited() {
332 return edited;
333 }
334
335 public MouseListener getMouseListener() {
336 return mouseListener;
337 }
338
339 public FocusListener getFocusListener() {
340 return focusListener;
341 }
342
343 public CmsEditable getCmsEditable() {
344 return cmsEditable;
345 }
346
347 @Override
348 public ISelection getSelection() {
349 return selection;
350 }
351 }