package org.eclipse.bpmn2.modeler.core.merrimac; import java.util.Hashtable; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.swt.custom.ScrolledComposite; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.widgets.Composite; import org.eclipse.ui.progress.UIJob; /** * This class handles redrawing of the entire Property Page Form contents. It * uses a UIJob to do this: multiple calls in quick succession (less than * UIJOB_INTERRUPT_INTERVAL milliseconds) will cause the job to be restarted. * This prevents the Form being redrawn multiple times even if no visible * changes have occurred since the last redraw. * * NOTE: All resize event listeners that call this class' redraw() method * MUST first call needsRedraw(). If this method returns false, the resize * listener MUST NOT call redraw(). */ public class Bpmn2PropertyPageRedrawHandler { private static int UIJOB_INTERRUPT_INTERVAL = 150; private static int NEEDS_REDRAW_INTERVAL = 1000; static Hashtable<Composite, Bpmn2PropertyPageRedrawHandler> handlers = new Hashtable<Composite, Bpmn2PropertyPageRedrawHandler>(); UIJob job; long lastRedrawTime; final Composite rootComposite; /** * Constructor for Redraw Handler. This is only created in response to * a redraw() request. * * @param rootComposite */ private Bpmn2PropertyPageRedrawHandler(final Composite rootComposite) { this.rootComposite = rootComposite; rootComposite.addDisposeListener(new DisposeListener() { @Override public void widgetDisposed(DisposeEvent e) { if (e.widget!=rootComposite) Bpmn2PropertyPageRedrawHandler.dispose(rootComposite); else if (e.widget instanceof Composite) Bpmn2PropertyPageRedrawHandler.dispose((Composite)e.widget); } }); } /** * Redraw the root composite that contains the given child. * * @param child */ public static void redraw(Composite child) { Bpmn2PropertyPageRedrawHandler handler = findHandler(child); if (handler!=null) handler.scheduleRedrawPage(); } /** * Check if the root composite that contains the given child needs * to be redrawn. This is calculated from the time of the last redraw * and prevents an infinite loop of redraws caused by resize listeners. * * @param child * @return */ public static boolean needsRedraw(Composite child) { Bpmn2PropertyPageRedrawHandler handler = findHandler(child); if (handler!=null) { return System.currentTimeMillis() - handler.lastRedrawTime > NEEDS_REDRAW_INTERVAL; } return false; } /** * Remove the redraw handler for the given root composite. * This prevents the static handler list from growing out of control. * * @param child */ public static void dispose(Composite child) { Bpmn2PropertyPageRedrawHandler handler = findHandler(child); if (handler!=null) { if (handler.job!=null) handler.job.cancel(); handlers.remove(handler.rootComposite); } } /** * Schedule a redraw. If the UIJob is already running but in a WAIT * state, cancel and reschedule it. */ private synchronized void scheduleRedrawPage() { if (job==null) { job = new UIJob("BPMN2 Property Page redraw") { //$NON-NLS-1$ @Override public IStatus runInUIThread(IProgressMonitor monitor) { try { doRedrawPage(); } catch (Exception e) { // this can happen if the editor is already // closed by the time the UI thread runs - ignore } return Status.OK_STATUS; } }; } if (job.getState() == Job.WAITING) job.cancel(); job.schedule(UIJOB_INTERRUPT_INTERVAL); } /** * Force a re-layout and redraw of the root composite by resizing. * This is the only way I've found of really forcing the Form widgets * to re-layout and redraw themselves. */ private void doRedrawPage() { if (!rootComposite.isDisposed()) { rootComposite.setRedraw(false); rootComposite.layout(); Point p = rootComposite.getSize(); p.x++; p.y++; rootComposite.setSize(p); p.x--; p.y--; rootComposite.setSize(p); rootComposite.setRedraw(true); lastRedrawTime = System.currentTimeMillis(); } } /** * Find the redraw handler for the child Composite. * * @param child * @return */ private static Bpmn2PropertyPageRedrawHandler findHandler(Composite child) { Bpmn2PropertyPageRedrawHandler handler = null; Composite composite = findRootComposite(child); if (composite!=null) { if (handlers.containsKey(composite)) handler = handlers.get(composite); else if (!composite.isDisposed()) { handler = new Bpmn2PropertyPageRedrawHandler(composite); handlers.put(composite, handler); } } return handler; } /** * Finds the top-level Composite that contains the child widget. * This is the parent of the ScrolledComposite which hosts the * property page Form. * * @param child * @return */ private static Composite findRootComposite(Composite child) { if (child!=null) { if (handlers.containsKey(child)) return child; Composite composite = child.getParent(); while (composite!=null && !(composite instanceof ScrolledComposite)) { composite = composite.getParent(); } if (composite!=null) composite = composite.getParent(); return composite; } return null; } }