/******************************************************************************* * Copyright (c) 2008 xored software, Inc. 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: * xored software, Inc. - initial API and Implementation (Yuri Strot) *******************************************************************************/ package com.xored.glance.ui.controls.items; import com.xored.glance.ui.sources.ColorManager; import com.xored.glance.ui.sources.ITextSourceListener; import com.xored.glance.ui.utils.TextUtils; import org.eclipse.core.runtime.ListenerList; import org.eclipse.jface.text.Region; import org.eclipse.jface.text.TextPresentation; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StyleRange; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.graphics.TextLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Item; import org.eclipse.swt.widgets.Listener; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; /** * @author Yuri Strot */ public class ItemDecorator implements Listener { public static final int DEFAULT_STYLE = SWT.BACKGROUND | SWT.FOREGROUND | SWT.SELECTED | SWT.HOT; protected Composite composite; protected ItemProvider provider; protected int style; protected TextLayout textLayout; protected Map<ItemCell, StyleRange[]> itemToMatches; protected Map<ItemCell, StyleRange[]> cacheStyles; protected List<ItemCell> cells; protected HashSet<ItemCell> cellSet; private ListenerList listeners = new ListenerList(); private boolean disposed; public ItemDecorator(Composite composite, ItemProvider provider) { this(composite, provider, DEFAULT_STYLE); } public ItemDecorator(Composite composite, ItemProvider provider, int style) { this.composite = composite; this.provider = provider; this.style = style; clearStyles(); init(); } public void addTextSourceListener(ITextSourceListener listener) { listeners.add(listener); } public void blocksChanged(ItemCell[] removed, ItemCell[] added) { for (ItemCell cell : removed) { cells.remove(cell); cellSet.remove(cell); } for (ItemCell cell : added) { cells.add(cell); cellSet.add(cell); cell.getItem().addListener(SWT.Dispose, this); } for (ITextSourceListener listener : getListeners()) { listener.blocksChanged(removed, added); } } public void clearStyles() { itemToMatches = new HashMap<ItemCell, StyleRange[]>(); cacheStyles = new HashMap<ItemCell, StyleRange[]>(); } public void dispose() { if (!disposed) { clearStyles(); composite.removeListener(SWT.PaintItem, this); composite.removeListener(SWT.EraseItem, this); disposed = true; redraw(); } } public void erase(Event event) { int style = SWT.BACKGROUND | SWT.FOREGROUND; if (!ColorManager.getInstance().isUseNative()) { style |= SWT.SELECTED | SWT.HOT; } event.detail &= ~style; } public List<ItemCell> getCells() { return cells; } public ITextSourceListener[] getListeners() { Object[] objects = listeners.getListeners(); ITextSourceListener[] listeners = new ITextSourceListener[objects.length]; System.arraycopy(objects, 0, listeners, 0, objects.length); return listeners; } @Override public void handleEvent(Event event) { switch (event.type) { case SWT.PaintItem: paint(event); break; case SWT.EraseItem: erase(event); break; case SWT.Dispose: if (event.widget instanceof Item) { ItemCell cell = new ItemCell((Item) event.widget, event.index, provider); blocksChanged(new ItemCell[] {cell}, new ItemCell[0]); } break; } } public boolean isDisposed() { return disposed; } public void redraw() { Rectangle rect = composite.getClientArea(); composite.redraw(rect.x, rect.y, rect.width, rect.height, true); } public void redraw(ItemCell cell) { Rectangle rect = provider.getBounds(cell.getItem(), cell.getIndex()); composite.redraw(rect.x, rect.y, rect.width, rect.height, true); } public void removeTextSourceListener(ITextSourceListener listener) { listeners.remove(listener); } public void setCells(List<ItemCell> cells) { this.cells = cells; for (ItemCell cell : cells) { cell.getItem().addListener(SWT.Dispose, this); } cellSet = new HashSet<ItemCell>(cells); } public void setStyles(ItemCell cell, StyleRange[] styles) { if (styles == null) { itemToMatches.remove(cell); } else { itemToMatches.put(cell, styles); } cacheStyles.put(cell, calculateStyles(cell)); } protected StyleRange[] calculateStyles(ItemCell cell) { StyleRange[] cellStyles = cell.getStyles(); StyleRange[] matchStyles = itemToMatches.get(cell); if (matchStyles == null || matchStyles.length == 0) { return cellStyles; } if (cellStyles.length == 0) { return matchStyles; } Region region = new Region(0, cell.getLength()); int size = cellStyles.length + matchStyles.length; TextPresentation presentation = new TextPresentation(region, size); presentation.replaceStyleRanges(cellStyles); presentation.mergeStyleRanges(matchStyles); return TextUtils.getStyles(presentation); } protected StyleRange[] getRanges(ItemCell cell) { StyleRange[] ranges = cacheStyles.get(cell); if (ranges == null) { ranges = calculateStyles(cell); cacheStyles.put(cell, ranges); } return ranges; } protected TextLayout getTextLayout() { if (textLayout == null) { int orientation = composite.getStyle() & (SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT); textLayout = new TextLayout(composite.getDisplay()); textLayout.setOrientation(orientation); } else { textLayout.setText(""); } return textLayout; } protected void init() { // FIXME composite.addListener(SWT.EraseItem, this); composite.addListener(SWT.PaintItem, this); redraw(); } protected void paint(Event event) { Item item = (Item) event.item; GC gc = event.gc; // remember colors to restore the GC later Color oldForeground = gc.getForeground(); Color oldBackground = gc.getBackground(); Color foreground = provider.getForeground(item, event.index); if (foreground != null) { gc.setForeground(foreground); } Color background = provider.getBackground(item, event.index); if (background != null) { gc.setBackground(background); } if (!ColorManager.getInstance().isUseNative() && (event.detail & SWT.SELECTED) != 0) { gc.setBackground(ColorManager.getInstance().getTreeSelectionBg()); gc.setForeground(ColorManager.getInstance().getTreeSelectionFg()); gc.fillRectangle(provider.getBounds(item, event.index)); } Image image = provider.getImage(item, event.index); if (image != null) { Rectangle imageBounds = provider.getImageBounds(item, event.index); if (imageBounds != null) { Rectangle bounds = image.getBounds(); // center the image in the given space int x = imageBounds.x + Math.max(0, (imageBounds.width - bounds.width) / 2); int y = imageBounds.y + Math.max(0, (imageBounds.height - bounds.height) / 2); gc.drawImage(image, x, y); } } Rectangle textBounds = provider.getTextBounds(item, event.index); if (textBounds != null) { TextLayout layout = getTextLayout(); layout.setText(provider.getText(item, event.index)); layout.setFont(provider.getFont(item, event.index)); ItemCell cell = new ItemCell(item, event.index, provider); if (!cellSet.contains(cell)) { blocksChanged(new ItemCell[0], new ItemCell[] {cell}); } StyleRange[] ranges = getRanges(cell); for (StyleRange range : ranges) { layout.setStyle(range, range.start, range.start + range.length - 1); } Rectangle layoutBounds = layout.getBounds(); int x = textBounds.x; int avg = (textBounds.height - layoutBounds.height) / 2; int y = textBounds.y + Math.max(0, avg); layout.draw(gc, x, y); } gc.setForeground(oldForeground); gc.setBackground(oldBackground); } }