/* * Copyright 2016 Nokia Solutions and Networks * Licensed under the Apache License, Version 2.0, * see license.txt file for details. */ package org.robotframework.red.nattable.edit; import org.eclipse.core.runtime.jobs.IJobChangeEvent; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.core.runtime.jobs.JobChangeAdapter; import org.eclipse.jface.fieldassist.ControlDecoration; import org.eclipse.jface.fieldassist.FieldDecorationRegistry; import org.eclipse.swt.SWT; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Text; import org.robotframework.red.graphics.ColorsManager; import org.robotframework.red.swt.SwtThread; import org.robotframework.red.swt.SwtThread.Evaluation; import com.google.common.annotations.VisibleForTesting; /** * @author Michal Anglart */ public class CellEditorValueValidationJobScheduler<V> { private final CellEditorValueValidator<V> validator; private CellEditorValueValidationJob<V> validationJob; private ControlDecoration decoration; private boolean isClosingLocked = false; CellEditorValueValidationJobScheduler(final CellEditorValueValidator<V> validator) { this.validator = validator; } void rescheduleValidation(final V value, final Control control) { isClosingLocked = true; if (validationJob != null && validationJob.getState() == Job.SLEEPING) { validationJob.cancel(); } validationJob = new CellEditorValueValidationJob<>(validator, value); validationJob.addJobChangeListener(new CellEditorValueValidationListener<>(control)); validationJob.schedule(300); } boolean canCloseCellEditor() { waitForJobToFinish(); return !isClosingLocked; } private void waitForJobToFinish() { final Job job = validationJob; if (job != null) { try { job.join(); } catch (final InterruptedException e) { throw new IllegalStateException("Cell editor validation job has been interrupted!", e); } } } public void armRevalidationOn(final Text textControl) { if (validator == null || textControl == null || textControl.isDisposed()) { return; } textControl.addModifyListener(new ModifyListener() { @SuppressWarnings("unchecked") @Override public void modifyText(final ModifyEvent e) { rescheduleValidation((V) textControl.getText(), textControl); } }); } @VisibleForTesting public CellEditorValueValidator<V> getValidator() { return this.validator; } @VisibleForTesting class CellEditorValueValidationListener<P> extends JobChangeAdapter { private final Control control; public CellEditorValueValidationListener(final Control control) { this.control = control; } @Override public void done(final IJobChangeEvent event) { if (event.getJob().getResult() == null) { return; } Object prop = event.getJob().getProperty(CellEditorValueValidationJob.getLockPropertyName()); if (prop instanceof Boolean) { isClosingLocked = (Boolean) prop; } final String errorMsg = (String) event.getJob() .getProperty(CellEditorValueValidationJob.getErrorMessagePropertyName()); SwtThread.syncExec(new Runnable() { @Override public void run() { if (decoration != null) { decoration.hide(); decoration.dispose(); } } }); decoration = SwtThread.syncEval(new Evaluation<ControlDecoration>() { @Override public ControlDecoration runCalculation() { if (control == null || control.isDisposed()) { return null; } if (errorMsg == null) { control.setForeground(control.getParent().getForeground()); return null; } control.setForeground(ColorsManager.getColor(255, 0, 0)); final ControlDecoration dec = new ControlDecoration(control, SWT.LEFT | SWT.TOP); dec.setDescriptionText(errorMsg); dec.setImage(FieldDecorationRegistry.getDefault() .getFieldDecoration(FieldDecorationRegistry.DEC_ERROR) .getImage()); return dec; } }); } } @VisibleForTesting ControlDecoration getDecoration() { return this.decoration; } }