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.apache.commons.logging.Log;
15 import org.apache.commons.logging.LogFactory;
16 import org.argeo.cms.CmsException;
17 import org.argeo.cms.ui.CmsEditable;
18 import org.argeo.cms.ui.widgets.ScrolledPage;
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
33 public abstract class AbstractPageViewer extends ContentViewer implements Observer {
34 private static final long serialVersionUID = 5438688173410341485L;
35
36 private final static Log log = LogFactory.getLog(AbstractPageViewer.class);
37
38 private final boolean readOnly;
39
40 private final Composite page;
41 private final CmsEditable cmsEditable;
42
43 private MouseListener mouseListener;
44 private FocusListener focusListener;
45
46 private EditablePart edited;
47 private ISelection selection = StructuredSelection.EMPTY;
48
49 private AccessControlContext accessControlContext;
50
51 protected AbstractPageViewer(Section parent, int style, CmsEditable cmsEditable) {
52
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
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 (Exception e) {
78 throw new CmsException("Cannot initialize model", e);
79 }
80 }
81
82
83 protected Boolean isModelInitialized(Node node) throws RepositoryException {
84 return true;
85 }
86
87
88 protected void initModel(Node node) throws RepositoryException {
89 }
90
91
92 protected MouseListener createMouseListener() {
93 return new MouseAdapter() {
94 private static final long serialVersionUID = 1L;
95 };
96 }
97
98
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 @Override
124 public void update(Observable o, Object arg) {
125 if (o == cmsEditable)
126 editingStateChanged(cmsEditable);
127 }
128
129
130 protected void refresh(Control control) throws RepositoryException {
131 }
132
133
134 protected void save(EditablePart part) throws RepositoryException {
135 }
136
137
138 protected void prepare(EditablePart part, Object caretPosition) {
139 }
140
141
142 protected void editingStateChanged(CmsEditable cmsEditable) {
143 }
144
145 @Override
146 public void refresh() {
147
148 Subject viewerSubject = getViewerSubject();
149 Subject.doAs(viewerSubject, (PrivilegedAction<Void>) () -> {
150 try {
151 if (cmsEditable.canEdit() && !readOnly)
152 mouseListener = createMouseListener();
153 else
154 mouseListener = null;
155 refresh(getControl());
156 layout(getControl());
157 } catch (RepositoryException e) {
158 throw new CmsException("Cannot refresh", e);
159 }
160 return null;
161 });
162 }
163
164 @Override
165 public void setSelection(ISelection selection, boolean reveal) {
166 this.selection = selection;
167 }
168
169 protected void updateContent(EditablePart part) throws RepositoryException {
170 }
171
172
173 protected void edit(EditablePart part, Object caretPosition) {
174 try {
175 if (edited == part)
176 return;
177
178 if (edited != null && edited != part) {
179 EditablePart previouslyEdited = edited;
180 try {
181 stopEditing(true);
182 } catch (Exception e) {
183 notifyEditionException(e);
184 edit(previouslyEdited, caretPosition);
185 return;
186 }
187 }
188
189 part.startEditing();
190 updateContent(part);
191 prepare(part, caretPosition);
192 edited = part;
193 layout(part.getControl());
194 } catch (RepositoryException e) {
195 throw new CmsException("Cannot edit " + part, e);
196 }
197 }
198
199 private void stopEditing(Boolean save) throws RepositoryException {
200 if (edited instanceof Widget && ((Widget) edited).isDisposed()) {
201 edited = null;
202 return;
203 }
204
205 assert edited != null;
206 if (edited == null) {
207 if (log.isTraceEnabled())
208 log.warn("Told to stop editing while not editing anything");
209 return;
210 }
211
212 if (save)
213 save(edited);
214
215 edited.stopEditing();
216 updateContent(edited);
217 layout(((EditablePart) edited).getControl());
218 edited = null;
219 }
220
221
222 protected void saveEdit() {
223 try {
224 if (edited != null)
225 stopEditing(true);
226 } catch (RepositoryException e) {
227 throw new CmsException("Cannot stop editing", e);
228 }
229 }
230
231 protected void cancelEdit() {
232 try {
233 if (edited != null)
234 stopEditing(false);
235 } catch (RepositoryException e) {
236 throw new CmsException("Cannot cancel editing", e);
237 }
238 }
239
240
241 public void layout(Control... controls) {
242 page.layout(controls);
243 }
244
245
246
247
248 protected EditablePart findDataParent(Control parent) {
249 if (parent instanceof EditablePart) {
250 return (EditablePart) parent;
251 }
252 if (parent.getParent() != null)
253 return findDataParent(parent.getParent());
254 else
255 throw new CmsException("No data parent found");
256 }
257
258
259
260 protected void checkEdited() {
261 if (edited == null || (edited instanceof Widget) && ((Widget) edited).isDisposed())
262 throw new CmsException("Edited should not be null or disposed at this stage");
263 }
264
265
266 protected void persistChanges(Session session) throws RepositoryException {
267 session.save();
268 session.refresh(false);
269
270 }
271
272
273 protected void persistChanges(Node anyNode) throws RepositoryException {
274 persistChanges(anyNode.getSession());
275 }
276
277
278 protected void notifyEditionException(Throwable e) {
279 Throwable eToLog = e;
280 if (e instanceof IllegalArgumentException)
281 if (e.getCause() instanceof SAXParseException)
282 eToLog = e.getCause();
283 log.error(eToLog.getMessage(), eToLog);
284
285
286
287 }
288
289 protected Subject getViewerSubject() {
290 Subject res = null;
291 if (accessControlContext != null) {
292 res = Subject.getSubject(accessControlContext);
293 }
294 if (res == null)
295 throw new CmsException("No subject associated with this viewer");
296 return res;
297 }
298
299
300 public boolean isReadOnly() {
301 return readOnly;
302 }
303
304 protected EditablePart getEdited() {
305 return edited;
306 }
307
308 public MouseListener getMouseListener() {
309 return mouseListener;
310 }
311
312 public FocusListener getFocusListener() {
313 return focusListener;
314 }
315
316 public CmsEditable getCmsEditable() {
317 return cmsEditable;
318 }
319
320 @Override
321 public ISelection getSelection() {
322 return selection;
323 }
324 }