/******************************************************************************* * Copyright (c) 2006-2013 The RCP Company and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * The RCP Company - initial API and implementation *******************************************************************************/ package com.rcpcompany.uibindings.internal.bindingMessages; import java.util.ArrayList; import java.util.List; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableItem; import com.rcpcompany.uibindings.BindingState; import com.rcpcompany.uibindings.IColumnBinding; import com.rcpcompany.uibindings.IColumnBindingCellInformation; import com.rcpcompany.uibindings.IContainerBinding; import com.rcpcompany.uibindings.IDisposable; import com.rcpcompany.uibindings.IValueBindingCell; import com.rcpcompany.uibindings.IViewerBinding; import com.rcpcompany.uibindings.internal.Activator; import com.rcpcompany.utils.logging.LogUtils; /** * {@link IWidgetDecoration} handler for a {@link IContainerBinding}. * * @author Tonny Madsen, The RCP Company */ public class ViewerBindingMessageDecorator implements IDisposable { /** * The viewer binding. */ protected final IViewerBinding myViewerBinding; /** * The control that backs the viewer. */ protected final Control myControl; /** * Margin width used between the decorator and the control. */ protected int myMarginWidth = 0; private final Listener myPaintItemListener = new Listener() { @Override public void handleEvent(Event event) { final IColumnBindingCellInformation cell = getViewerBinding().getCell( event.index - getViewerBinding().getFirstTableColumnOffset(), event.item.getData()); if (cell == null) return; decorate(cell, event); } }; /** * Constructs and returns a new decoration for the specified viewer. * * @param viewerBinding the viewer binding */ public ViewerBindingMessageDecorator(IViewerBinding viewerBinding) { if (Activator.getDefault().TRACE_LIFECYCLE_VIEWER_BINDING_MESSAGE_DECORATOR) { LogUtils.debug(this, hashCode() + ": " + viewerBinding); //$NON-NLS-1$ } myViewerBinding = viewerBinding; myControl = viewerBinding.getControl(); getViewerBinding().registerService(this); getControl().addListener(SWT.PaintItem, myPaintItemListener); } @Override public void dispose() { if (Activator.getDefault().TRACE_LIFECYCLE_VIEWER_BINDING_MESSAGE_DECORATOR) { LogUtils.debug(this, hashCode() + ": " + getViewerBinding()); //$NON-NLS-1$ } getControl().removeListener(SWT.PaintItem, myPaintItemListener); // TODO getViewerBinding().unregisterService(this); } /** * Returns the binding of this decorator. * * @return the binding */ public IViewerBinding getViewerBinding() { return myViewerBinding; } /** * Returns the control that backs the viewer of this decoration. * * @return the control */ public Control getControl() { return myControl; } /** * Constructs and returns a new widget decoration for the specified cell. * * @param cell the cell in question * @param position TODO * @return the widget decoration */ public IWidgetDecoration addCellDecoration(IColumnBindingCellInformation cell, int position) { final CellDecoration d = new CellDecoration(cell, position); return d; } /** * Decorates the specified cell with all registered decorations. * * @param cell the cell to decorate * @param event the event of the paint */ public void decorate(IColumnBindingCellInformation cell, Event event) { for (final CellDecoration cd : myCellDecorations) { if (!cd.isCell(cell)) { continue; } cd.decorate(event); } } /** * List with all current cell decorations. */ protected List<CellDecoration> myCellDecorations = new ArrayList<CellDecoration>(); /** * A decoration for a specific {@link IValueBindingCell}. * <p> * Note that there can be multiple decorations for a specific cell. * * @author Tonny Madsen, The RCP Company */ protected class CellDecoration implements IWidgetDecoration { /** * The cell of this decoration. */ private final IColumnBindingCellInformation myCell; /** * Returns the cell of this decoration. * * @return the cell */ public IColumnBindingCellInformation getCell() { return myCell; } /** * The text of this decoration. */ private String myText; @Override public String getDescriptionText() { return myText; } @Override public void setDescriptionText(String text) { myText = text; } /** * The image of this decoration. */ private Image myImage; @Override public Image getImage() { return myImage; } @Override public void setImage(Image image) { myImage = image; update(); } /** * The area of this cell that needs to be redrawn if any change happens. */ private Rectangle myDecorationArea; /** * Returns the area of this cell that needs to be redrawn if any change happens. * * @return a rectangle with the area or <code>null</code> */ public Rectangle getDecorationArea() { return myDecorationArea; } /** * The position of the decoration. */ private final int myPosition; /** * Returns the position of the decoration. * * @return the position */ public int getPosition() { return myPosition; } /** * Constructs and returns a new cell decoration. * * @param cell the cell in question * @param position the wanted position of the decoration */ protected CellDecoration(IColumnBindingCellInformation cell, int position) { myCell = cell; myPosition = position; myCellDecorations.add(this); if (Activator.getDefault().TRACE_LIFECYCLE_VIEWER_BINDING_MESSAGE_DECORATOR) { LogUtils.debug(this, hashCode() + ": " + getCell()); //$NON-NLS-1$ } } @Override public void dispose() { if (shouldShowDecoration()) { hide(); /* * Only update the control if it is not disposed and the viewer binding is still OK */ if (!getControl().isDisposed() && getViewerBinding().getState() == BindingState.OK) { getControl().update(); } } if (Activator.getDefault().TRACE_LIFECYCLE_VIEWER_BINDING_MESSAGE_DECORATOR) { LogUtils.debug(this, hashCode() + ": " + getCell()); //$NON-NLS-1$ } myCellDecorations.remove(this); } /** * Returns whether the specified cell is identical to the cell of this decoration. * * @param cell the cell to test * @return <code>true</code> if the cells are identical. */ public boolean isCell(IColumnBindingCellInformation cell) { return cell == myCell; } /** * Decorates the current cell as needed. * * @param event the cell to decorate */ public void decorate(Event event) { if (Activator.getDefault().TRACE_LIFECYCLE_VIEWER_BINDING_MESSAGE_DECORATOR) { LogUtils.debug(this, hashCode() + ": " + getCell()); //$NON-NLS-1$ } final Rectangle rect = calulateDecorationArea(event); // LogUtils.debug(this, "\nclipping: " + event.gc.getClipping() + "\narea: " + rect); if (shouldShowDecoration()) { event.gc.drawImage(getImage(), rect.x, rect.y); } // event.gc.drawRectangle(rect); // TODO update area } /** * Calculates the area of the decoration based on the paint event object. * * @param event the paint event object * @return the new area */ public Rectangle calulateDecorationArea(Event event) { // Compute the bounds first relative to the event clipping. final Rectangle imageBounds = getImage().getBounds(); final Rectangle cellBounds = event.gc.getClipping(); int x, y; // Compute x if ((getPosition() & SWT.RIGHT) == SWT.RIGHT) { x = cellBounds.x + cellBounds.width + myMarginWidth - imageBounds.width; } else { // default is left x = cellBounds.x - myMarginWidth; } // Compute y if ((getPosition() & SWT.TOP) == SWT.TOP) { y = cellBounds.y; } else if ((getPosition() & SWT.BOTTOM) == SWT.BOTTOM) { y = cellBounds.y + cellBounds.height - imageBounds.height; } else { // default is center y = cellBounds.y + (cellBounds.height - imageBounds.height) / 2; } myDecorationArea = new Rectangle(x, y, imageBounds.width, imageBounds.height); return getDecorationArea(); } /** * Requests an update of the area of this cell decoration. */ public void update() { Rectangle area = getDecorationArea(); if (area == null) { final IColumnBinding column = getCell().getColumn(); final IViewerBinding viewer = column.getViewerBinding(); if (viewer.getControl() instanceof Table) { final Table t = (Table) viewer.getControl(); final int rowNo = viewer.getList().indexOf(getCell().getElement()); /* * Timing! We can request an element that does not exist any more or has not * been created in the table yet... */ if (rowNo == -1 || rowNo >= t.getItemCount()) return; final TableItem item = t.getItem(rowNo); area = item.getBounds(viewer.getColumns().indexOf(column) + viewer.getFirstTableColumnOffset()); } } if (Activator.getDefault().TRACE_LIFECYCLE_VIEWER_BINDING_MESSAGE_DECORATOR) { LogUtils.debug(this, hashCode() + ": " + getCell() + "\narea: " + area); //$NON-NLS-1$ //$NON-NLS-2$ } if (area != null) { getControl().redraw(area.x, area.y, area.width, area.height, true); } } /** * Whether the decoration is shown. */ private boolean myVisible = false; @Override public void hide() { if (myVisible) { myVisible = false; update(); } } @Override public void show() { if (!myVisible) { myVisible = true; update(); } } /* * Return true if the decoration should be shown, false if it should not. */ private boolean shouldShowDecoration() { if (!myVisible) return false; if (getControl() == null || getControl().isDisposed() || getImage() == null) return false; if (!getControl().isVisible()) return false; // if (showOnlyOnFocus) { // return hasFocus; // } return true; } } }