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