Skip to content

Package: AbstractSWTRenderer

AbstractSWTRenderer

nameinstructionbranchcomplexitylinemethod
AbstractSWTRenderer(VElement, ViewModelContext, ReportService)
M: 0 C: 6
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 2
100%
M: 0 C: 1
100%
applyEnable()
M: 0 C: 1
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
applyReadOnly()
M: 0 C: 1
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
applyValidation()
M: 0 C: 1
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
applyValidation(VDiagnostic, VDiagnostic)
M: 0 C: 1
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
applyVisible()
M: 2 C: 75
97%
M: 2 C: 8
80%
M: 2 C: 4
67%
M: 0 C: 13
100%
M: 0 C: 1
100%
canReveal(Control)
M: 12 C: 0
0%
M: 6 C: 0
0%
M: 4 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
dispose()
M: 0 C: 21
100%
M: 1 C: 1
50%
M: 1 C: 1
50%
M: 0 C: 7
100%
M: 0 C: 1
100%
finalizeRendering(Composite)
M: 0 C: 38
100%
M: 0 C: 8
100%
M: 0 C: 5
100%
M: 0 C: 12
100%
M: 0 C: 1
100%
getControls()
M: 0 C: 11
100%
M: 0 C: 2
100%
M: 0 C: 2
100%
M: 0 C: 3
100%
M: 0 C: 1
100%
getDefaultFontName(Control)
M: 0 C: 8
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
ignoreEnableOnReadOnly()
M: 0 C: 4
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
init()
M: 0 C: 29
100%
M: 1 C: 1
50%
M: 1 C: 1
50%
M: 0 C: 8
100%
M: 0 C: 1
100%
isRenderingFinished()
M: 0 C: 3
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
postInit()
M: 0 C: 1
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
preInit()
M: 0 C: 1
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
M: 0 C: 1
100%
render(SWTGridCell, Composite)
M: 2 C: 32
94%
M: 1 C: 3
75%
M: 1 C: 2
67%
M: 1 C: 8
89%
M: 0 C: 1
100%
scrollToReveal()
M: 0 C: 34
100%
M: 2 C: 8
80%
M: 2 C: 4
67%
M: 0 C: 9
100%
M: 0 C: 1
100%
scrollToReveal(Control)
M: 0 C: 21
100%
M: 0 C: 4
100%
M: 0 C: 3
100%
M: 0 C: 5
100%
M: 0 C: 1
100%
setControlEnabled(SWTGridCell, Control, boolean)
M: 0 C: 4
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 2
100%
M: 0 C: 1
100%

Coverage

1: /*******************************************************************************
2: * Copyright (c) 2011-2019 EclipseSource Muenchen GmbH and others.
3: *
4: * All rights reserved. This program and the accompanying materials
5: * are made available under the terms of the Eclipse Public License 2.0
6: * which accompanies this distribution, and is available at
7: * https://www.eclipse.org/legal/epl-2.0/
8: *
9: * SPDX-License-Identifier: EPL-2.0
10: *
11: * Contributors:
12: * Edagr Mueller - initial API and implementation
13: * Eugen Neufeld - Refactoring
14: * Christian W. Damus - bugs 527686, 548592
15: ******************************************************************************/
16: package org.eclipse.emfforms.spi.swt.core;
17:
18: import java.util.Arrays;
19: import java.util.Collection;
20: import java.util.Collections;
21: import java.util.Iterator;
22: import java.util.LinkedHashMap;
23: import java.util.LinkedHashSet;
24: import java.util.Map;
25: import java.util.Set;
26:
27: import org.eclipse.emf.common.util.AbstractTreeIterator;
28: import org.eclipse.emf.ecp.view.model.common.AbstractRenderer;
29: import org.eclipse.emf.ecp.view.spi.context.ViewModelContext;
30: import org.eclipse.emf.ecp.view.spi.model.ModelChangeListener;
31: import org.eclipse.emf.ecp.view.spi.model.ModelChangeNotification;
32: import org.eclipse.emf.ecp.view.spi.model.VDiagnostic;
33: import org.eclipse.emf.ecp.view.spi.model.VElement;
34: import org.eclipse.emf.ecp.view.spi.model.VViewPackage;
35: import org.eclipse.emf.ecp.view.spi.renderer.NoPropertyDescriptorFoundExeption;
36: import org.eclipse.emf.ecp.view.spi.renderer.NoRendererFoundException;
37: import org.eclipse.emfforms.spi.common.report.ReportService;
38: import org.eclipse.emfforms.spi.swt.core.layout.EMFFormsSWTLayoutUtil;
39: import org.eclipse.emfforms.spi.swt.core.layout.SWTGridCell;
40: import org.eclipse.emfforms.spi.swt.core.layout.SWTGridDescription;
41: import org.eclipse.swt.custom.ScrolledComposite;
42: import org.eclipse.swt.events.DisposeEvent;
43: import org.eclipse.swt.events.DisposeListener;
44: import org.eclipse.swt.layout.GridData;
45: import org.eclipse.swt.widgets.Composite;
46: import org.eclipse.swt.widgets.Control;
47:
48: /**
49: * Common base class for all SWT specific renderer classes.
50: *
51: * A renderer using other renderers to render its contents must call this methods in this order:
52: *
53: * <pre>
54: * {@link #getGridDescription(SWTGridDescription)}
55: * for each SWTGridCell
56: *         {@link #render(SWTGridCell, Composite)}
57: * {@link #finalizeRendering(Composite)}
58: * </pre>
59: *
60: * If you don't call {@link #finalizeRendering(Composite)} after the rendering, the automatic disposing of the renderer
61: * will not work, as well as the initial validation check.
62: *
63: * @author Eugen Neufeld
64: *
65: * @param <VELEMENT> the actual type of the {@link VElement} to be drawn
66: * @since 1.2
67: */
68: public abstract class AbstractSWTRenderer<VELEMENT extends VElement> extends AbstractRenderer<VELEMENT> {
69:
70:         /**
71:          * Variant constant for indicating RAP controls.
72:          */
73:         protected static final String CUSTOM_VARIANT = "org.eclipse.rap.rwt.customVariant"; //$NON-NLS-1$
74:         private ModelChangeListener listener;
75:         private Map<SWTGridCell, Control> controls;
76:         private boolean renderingFinished;
77:
78:         /**
79:          * Default Constructor.
80:          *
81:          * @param vElement the view element to be rendered
82:          * @param viewContext The view model context
83:          * @param reportService the ReportService to use
84:          * @since 1.6
85:          */
86:         public AbstractSWTRenderer(final VELEMENT vElement, final ViewModelContext viewContext,
87:                 ReportService reportService) {
88:                 super(vElement, viewContext, reportService);
89:         }
90:
91:         /**
92:          * Returns the GridDescription for this Renderer.
93:          *
94:          * @param gridDescription the current {@link SWTGridDescription}
95:          * @return the number of controls per row
96:          * @since 1.3
97:          */
98:         public abstract SWTGridDescription getGridDescription(SWTGridDescription gridDescription);
99:
100:         /**
101:          * Initializes the {@link AbstractSWTRenderer}.
102:          *
103:          * @since 1.6
104:          */
105:         public final void init() {
106:                 preInit();
107:                 controls = new LinkedHashMap<SWTGridCell, Control>();
108:•                if (getViewModelContext() != null) {
109:                         listener = new ViewChangeListener();
110:                         getViewModelContext().registerViewChangeListener(listener);
111:                 }
112:                 getViewModelContext().addContextUser(this);
113:
114:                 postInit();
115:         }
116:
117:         /**
118:          * Returns a copy of the {@link GridCell} to {@link Control} map.
119:          *
120:          * @return a copy of the controls map
121:          * @since 1.3
122:          */
123:         protected final Map<SWTGridCell, Control> getControls() {
124:•                if (controls == null) {
125:                         return Collections.emptyMap();
126:                 }
127:                 return new LinkedHashMap<SWTGridCell, Control>(controls);
128:         }
129:
130:         /**
131:          * Use this method to initialize objects which are needed already before rendering.
132:          *
133:          * @since 1.3
134:          */
135:         protected void preInit() {
136:
137:         }
138:
139:         /**
140:          * Use this method to initialize objects which are needed during rendering.
141:          *
142:          * @since 1.3
143:          */
144:         protected void postInit() {
145:
146:         }
147:
148:         /**
149:          * Disposes all resources used by the renderer.
150:          * Don't forget to call super.dispose if overwriting this method.
151:          *
152:          * @since 1.3
153:          */
154:         @Override
155:         protected void dispose() {
156:•                if (getViewModelContext() != null) {
157:                         getViewModelContext().unregisterViewChangeListener(listener);
158:                 }
159:                 listener = null;
160:                 controls = null;
161:                 getViewModelContext().removeContextUser(this);
162:
163:                 super.dispose();
164:         }
165:
166:         /**
167:          * Renders the passed {@link VElement}.
168:          *
169:          * @param cell the {@link SWTGridCell} of the control to render
170:          * @param parent the {@link Composite} to render on
171:          * @return the rendered {@link Control}
172:          * @throws NoRendererFoundException this is thrown when a renderer cannot be found
173:          * @throws NoPropertyDescriptorFoundExeption this is thrown when no property descriptor can be found
174:          * @since 1.3
175:          */
176:         public Control render(final SWTGridCell cell, Composite parent)
177:                 throws NoRendererFoundException, NoPropertyDescriptorFoundExeption {
178:
179:                 Control control = controls.get(cell);
180:•                if (control != null) {
181:                         return control;
182:                 }
183:
184:                 control = renderControl(cell, parent);
185:•                if (control == null) {
186:                         // something went wrong, log
187:                         return null;
188:                 }
189:                 controls.put(cell, control);
190:
191:                 // register dispose listener to rerender if disposed
192:                 control.addDisposeListener(new DisposeListener() {
193:
194:                         @Override
195:                         public void widgetDisposed(DisposeEvent e) {
196:                                 if (controls != null) {
197:                                         controls.remove(cell);
198:                                 }
199:                         }
200:                 });
201:
202:                 return control;
203:         }
204:
205:         /**
206:          * Called by the framework to initialize listener.
207:          *
208:          * @param parent the parent used during render
209:          * @since 1.3
210:          */
211:         public void finalizeRendering(Composite parent) {
212:•                if (renderingFinished) {
213:                         return;
214:                 }
215:                 renderingFinished = true;
216:
217:                 // Apply visibility, enablement, and validation decorations
218:•                if (!getVElement().isVisible()) {
219:                         /* convention is to render visible, so only call apply if we are invisible */
220:                         applyVisible();
221:                 }
222:                 applyReadOnly();
223:•                if (!ignoreEnableOnReadOnly()) {
224:                         applyEnable();
225:                 }
226:                 applyValidation();
227:
228:                 // When some control is disposed, dispose me. Don't worry about
229:                 // my parent composite because I could be a detail view that is cached
230:                 // independently of it in a master-detail situation
231:•                if (!controls.isEmpty()) {
232:                         controls.values().iterator().next().addDisposeListener(new DisposeListener() {
233:
234:                                 @Override
235:                                 public void widgetDisposed(DisposeEvent event) {
236:                                         dispose();
237:                                 }
238:                         });
239:                 }
240:         }
241:
242:         /**
243:          * Query whether rendering has completed and I am ready for user interaction.
244:          *
245:          * @return whether rendering has finished
246:          *
247:          * @since 1.22
248:          */
249:         protected boolean isRenderingFinished() {
250:                 return renderingFinished;
251:         }
252:
253:         /**
254:          * Returns <code>true</code> when read only will always force control to be disabled.
255:          *
256:          * @return <code>true</code> when read only will always force control to be disabled.
257:          */
258:         protected boolean ignoreEnableOnReadOnly() {
259:                 return getVElement().isEffectivelyReadonly();
260:         }
261:
262:         /**
263:          * Renders the passed {@link VElement}.
264:          *
265:          * @param cell the {@link GridCell} of the control to render
266:          * @param parent the {@link Composite} to render on
267:          * @return the rendered {@link Control}
268:          * @throws NoRendererFoundException this is thrown when a renderer cannot be found
269:          * @throws NoPropertyDescriptorFoundExeption this is thrown when no property descriptor can be found
270:          * @since 1.3
271:          */
272:         protected abstract Control renderControl(SWTGridCell cell, Composite parent) throws NoRendererFoundException,
273:                 NoPropertyDescriptorFoundExeption;
274:
275:         /**
276:          * Marks a controls as readonly.
277:          *
278:          * @since 1.3
279:          *
280:          */
281:         protected void applyReadOnly() {
282:                 // Do nothing, implement behavior in implementing class if needed
283:         }
284:
285:         /**
286:          * Allows implementers to set a control to enabled.
287:          *
288:          * @since 1.3
289:          *
290:          */
291:         protected void applyEnable() {
292:                 // Do nothing, implement behavior in implementing class if needed
293:         }
294:
295:         /**
296:          * Wraps the call to enable/disable a control.
297:          *
298:          * @param gridCell the {@link SWTGridCell} to enable/disable
299:          * @param control the {@link Control} to enable/disable
300:          * @param enabled true if control should be enabled, false otherwise
301:          * @since 1.3
302:          */
303:         protected void setControlEnabled(SWTGridCell gridCell, Control control, boolean enabled) {
304:                 control.setEnabled(enabled);
305:         }
306:
307:         /**
308:          * Allows implementers to check and set the visibility on the whole result row.
309:          *
310:          * @since 1.3
311:          *
312:          */
313:         protected void applyVisible() {
314:                 final boolean visible = getVElement().isVisible();
315:                 /* avoid multiple layout calls by saving the parents which need to be relayouted */
316:                 final Set<Composite> parents = new LinkedHashSet<Composite>();
317:•                for (final SWTGridCell gridCell : controls.keySet()) {
318:                         final Object layoutData = controls.get(gridCell).getLayoutData();
319:•                        if (GridData.class.isInstance(layoutData)) {
320:                                 final GridData gridData = (GridData) layoutData;
321:•                                if (gridData != null) {
322:•                                        gridData.exclude = !visible;
323:                                 }
324:                         }
325:                         controls.get(gridCell).setVisible(visible);
326:                         parents.add(controls.get(gridCell).getParent());
327:                 }
328:•                for (final Composite composite : parents) {
329:                         EMFFormsSWTLayoutUtil.adjustParentSize(composite.getChildren()[0]);
330:                 }
331:         }
332:
333:         /**
334:          * Allows implementers to display the validation state of the control.
335:          * The default implementation does nothing.
336:          *
337:          * @since 1.3
338:          */
339:         protected void applyValidation() {
340:
341:         }
342:
343:         /**
344:          * Called before the {@link #applyValidation()}. This method allows to create a diff between the old diagnostic and
345:          * the new diagnostic and thus improve the performance of the overlay apply by triggering it only on the relevant
346:          * elements.
347:          *
348:          * @param oldDiagnostic The previous {@link VDiagnostic}
349:          * @param newDiagnostic The current {@link VDiagnostic}
350:          * @since 1.14
351:          */
352:         protected void applyValidation(VDiagnostic oldDiagnostic, VDiagnostic newDiagnostic) {
353:
354:         }
355:
356:         /**
357:          * @return String the default font name on the system.
358:          * @param control The control to derive the default font name from
359:          *
360:          * @since 1.5
361:          */
362:         protected String getDefaultFontName(Control control) {
363:                 return control.getDisplay().getSystemFont().getFontData()[0].getName();
364:         }
365:
366:         /**
367:          * If my control is rendered within a scrolled composite, scroll that composite to reveal me.
368:          *
369:          * @since 1.22
370:          */
371:         public void scrollToReveal() {
372:•                if (controls != null && !controls.isEmpty()) {
373:                         @SuppressWarnings("serial")
374:                         final Iterator<Control> iter = new AbstractTreeIterator<Control>(controls.values(), false) {
375:                                 @Override
376:                                 protected Iterator<? extends Control> getChildren(Object object) {
377:                                         if (object instanceof Composite) {
378:                                                 return Arrays.asList(((Composite) object).getChildren()).iterator();
379:                                         }
380:                                         if (object instanceof Collection<?>) {
381:                                                 @SuppressWarnings("unchecked")
382:                                                 final Collection<? extends Control> collection = (Collection<? extends Control>) object;
383:                                                 return collection.iterator();
384:                                         }
385:                                         return Collections.emptyIterator();
386:                                 }
387:                         };
388:
389:•                        while (iter.hasNext()) {
390:                                 final Control next = iter.next();
391:•                                if (next instanceof Composite) {
392:                                         // Don't attempt to reveal composites
393:                                         continue;
394:                                 }
395:
396:•                                if (scrollToReveal(next)) {
397:                                         break;
398:                                 }
399:                         }
400:                 }
401:         }
402:
403:         /**
404:          * Scroll composites as necessary to reveal the given {@code control} and
405:          * then request focus.
406:          *
407:          * @param control the control to reveal and focus
408:          * @return whether the focus was successfully set (usually because the control
409:          * is one that can receive input focus)
410:          *
411:          * @since 1.22
412:          */
413:         protected boolean scrollToReveal(final Control control) {
414:•                for (Composite parent = control.getParent(); parent != null; parent = parent.getParent()) {
415:•                        if (parent instanceof ScrolledComposite) {
416:                                 final ScrolledComposite scrolled = (ScrolledComposite) parent;
417:                                 scrolled.showControl(control);
418:                         }
419:                 }
420:
421:                 // Try to request focus
422:                 return control.setFocus();
423:         }
424:
425:         /**
426:          * Query whether a given {@code control} can plausibly be revealed.
427:          *
428:          * @param control a control to be revealed
429:          * @return whether it reasonably can be revealed
430:          *
431:          * @since 1.22
432:          */
433:         protected boolean canReveal(Control control) {
434:•                return control != null && !control.isDisposed() && control.isVisible();
435:         }
436:
437:         //
438:         // Nested types
439:         //
440:
441:         /**
442:          * A listener that reacts to view-model changes to update the UI accordingly.
443:          * Examples include updating enablement/visibility state and diagnostic decorations.
444:          */
445:         private final class ViewChangeListener implements ModelChangeListener {
446:                 @Override
447:                 public void notifyChange(ModelChangeNotification notification) {
448:                         if (!renderingFinished) {
449:                                 return;
450:                         }
451:
452:                         // We ned to handle touch events for the diagnostic if in the case of
453:                         // re-use of the view model we update diagnostics in situ
454:                         if (notification.getRawNotification().isTouch() &&
455:                                 notification.getStructuralFeature() != VViewPackage.Literals.ELEMENT__DIAGNOSTIC) {
456:                                 return;
457:                         }
458:                         // Always apply enable and read-only if it changed because it might have changed for a parent
459:                         if (notification.getStructuralFeature() == VViewPackage.Literals.ELEMENT__ENABLED) {
460:                                 if (!ignoreEnableOnReadOnly()) {
461:                                         applyEnable();
462:                                 }
463:                         } else if (notification.getStructuralFeature() == VViewPackage.Literals.ELEMENT__READONLY) {
464:                                 applyReadOnly();
465:                         }
466:                         if (notification.getNotifier() != getVElement()) {
467:                                 return;
468:                         }
469:                         if (notification.getStructuralFeature() == VViewPackage.Literals.ELEMENT__VISIBLE) {
470:                                 applyVisible();
471:                         } else if (notification.getStructuralFeature() == VViewPackage.Literals.ELEMENT__DIAGNOSTIC) {
472:                                 final VDiagnostic newDia = (VDiagnostic) notification.getRawNotification().getNewValue();
473:                                 final VDiagnostic oldDia = (VDiagnostic) notification.getRawNotification().getOldValue();
474:                                 applyValidation(oldDia, newDia);
475:                                 applyValidation();
476:                         }
477:                 }
478:         }
479:
480: }