/*- * Copyright © 2009 Diamond Light Source Ltd. * * This file is part of GDA. * * GDA is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License version 3 as published by the Free * Software Foundation. * * GDA is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along * with GDA. If not, see <http://www.gnu.org/licenses/>. */ package uk.ac.gda.common.rcp.util; import java.util.Arrays; import java.util.HashSet; import java.util.Set; import java.util.Stack; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; /** * Class to deal with setting items in a grid layout invisible. Allows batch updating of controls to reduce flicker */ public class GridUtils { private static int depthCount = 0; /** FIXME - static collections with widgets in are a bad idea, causes memory leaks **/ private static Set<Control> controlsToLayout = new HashSet<Control>(); /** FIXME - static collections with widgets in are a bad idea, causes memory leaks **/ private static Stack<Control> controlsToRedraw = new Stack<Control>(); /** * Start a batch layout update. Wrap multiple calls to setVisibleAndLayout or layout with start/end MultLayout. * <p> * Calls to startMultiLayout can be nested as a stack is used internally. The last call to endMultiLayout causes all * the layouts to happen at once. * </p> * * @param parent * a suitable control that encompasses all the controls that may have their visibility changed. parent is * optional and can be null. If parent is "wrong" or null the effect is simply to have slightly more * flicker in the UI that optimal */ public static void startMultiLayout(Control parent) { if (parent != null) parent.setRedraw(false); controlsToRedraw.push(parent); depthCount++; } /** * End a batch layout update. If the stack is empty, the all the controls that have had layout updated will now be * laid out and redrawn. */ public static void endMultiLayout() { depthCount--; if (depthCount == 0 && controlsToLayout.size() > 0) { Control[] controls = controlsToLayout.toArray(new Control[controlsToLayout.size()]); controls[0].getShell().layout(controls); for (Control control : controls) { control.setRedraw(true); } controlsToLayout.clear(); } Control parent = controlsToRedraw.pop(); if (parent != null) parent.setRedraw(true); } /** * Changes visibility and layout of a control. Takes into consideration excluding the control from the GridData * layout manager. * <p> * If this function is called within the scope of startMultiLayout, the final steps of layout will not take effect * until endMultiLayout is called. * </p> * * @param widget * the widget to make visible or invisible * @param isVisible * is true to make widget visible, false to hide it */ public static void setVisibleAndLayout(final Control widget, final boolean isVisible) { if (widget == null) return; if (!(widget.getLayoutData() instanceof GridData)) { throw new IllegalArgumentException("Widget must have GridData layout data applied"); } final GridData data = (GridData) widget.getLayoutData(); if (data.exclude != !isVisible || widget.getVisible() != isVisible) { data.exclude = !isVisible; if (depthCount == 0) { // perform update immediately widget.setVisible(isVisible); try { //changed from widget.getShell() to widget.getParent() as the former //led to views not laying out correctly when opened in running workbench despite working //ok if view is opened during workbench initialisation widget.getParent().layout(new Control[] { widget }); } catch (Exception ignored) { // If we cannot layout parent then not a problem. } } else { // defer update until endMultiUpdate is called // don't turn off redraw multiple times for the same // widget because we only turn it back on once. // This is important since setRedraw is stacked if (!controlsToLayout.contains(widget)) { widget.setRedraw(false); } widget.setVisible(isVisible); controlsToLayout.add(widget); } } } /** * Simplified version of setVisibleAndLayout(...) which cannot cause * a memory leak. Does not work with startMultiLayout() and endMultiLayout() * * You need to call layout once on the parent widget after using this method. For instance: * * GridUtils.setVisible(wLabel, wVisible); GridUtils.setVisible(w, wVisible); GridUtils.setVisible(kLabel, kVisible); GridUtils.setVisible(kStart, kVisible); getShell().layout(); * * @param widget * @param isVisible */ public static void setVisible(final Control widget, final boolean isVisible) { if (widget == null) return; if (widget.getLayoutData() instanceof GridData) { final GridData data = (GridData) widget.getLayoutData(); data.exclude = !isVisible; } widget.setVisible(isVisible); } /** * Calls layout on the control, deferring the call if within a start/endMultiLayout call. Use this version of layout * only when you are sure that the bounding box has not changed, otherwise, use layoutFull * * @param control * is the widget to re-layout */ public static void layout(Composite control) { if (depthCount == 0) { control.layout(); } else { controlsToLayout.add(control); controlsToLayout.addAll(Arrays.asList(control.getChildren())); } } /** * Calls layout on the control, deferring the call if within a start/endMultiLayout call. As opposed to layout(), * this forces a full layout. * * @param control * is the widget to re-layout */ public static void layoutFull(Composite control) { if (depthCount == 0) { try { control.setRedraw(false); control.getShell().layout(new Control[] { control }); control.layout(); } finally { control.setRedraw(true); } } else { controlsToLayout.add(control); controlsToLayout.addAll(Arrays.asList(control.getChildren())); } } /** * * @param area */ public static void removeMargins(Composite area) { final GridLayout layout = (GridLayout)area.getLayout(); layout.horizontalSpacing=0; layout.verticalSpacing =0; layout.marginBottom =0; layout.marginTop =0; layout.marginLeft =0; layout.marginRight =0; layout.marginHeight =0; layout.marginWidth =0; } }