1 /******************************************************************************* 2 * Copyright (c) 2008, 2016 IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * IBM Corporation - initial API and implementation 13 *******************************************************************************/ 14 package org.eclipse.jface.internal.text; 15 16 import org.eclipse.swt.SWT; 17 import org.eclipse.swt.custom.StyleRange; 18 import org.eclipse.swt.graphics.Color; 19 import org.eclipse.swt.graphics.GC; 20 import org.eclipse.swt.graphics.Image; 21 import org.eclipse.swt.graphics.Rectangle; 22 import org.eclipse.swt.graphics.TextLayout; 23 import org.eclipse.swt.widgets.Event; 24 import org.eclipse.swt.widgets.Listener; 25 import org.eclipse.swt.widgets.Table; 26 import org.eclipse.swt.widgets.TableItem; 27 28 29 /** 30 * Adds owner draw support for tables. 31 * 32 * @since 3.4 33 */ 34 public class TableOwnerDrawSupport implements Listener { 35 36 private static final String STYLED_RANGES_KEY= "styled_ranges"; //$NON-NLS-1$ 37 38 // shared text layout 39 private TextLayout fSharedLayout; 40 41 private int fDeltaOfLastMeasure; 42 install(Table table)43 public static void install(Table table) { 44 TableOwnerDrawSupport listener= new TableOwnerDrawSupport(table); 45 table.addListener(SWT.Dispose, listener); 46 table.addListener(SWT.MeasureItem, listener); 47 table.addListener(SWT.EraseItem, listener); 48 table.addListener(SWT.PaintItem, listener); 49 } 50 51 /** 52 * Stores the styled ranges in the given table item. 53 * 54 * @param item table item 55 * @param column the column index 56 * @param ranges the styled ranges or <code>null</code> to remove them 57 */ storeStyleRanges(TableItem item, int column, StyleRange[] ranges)58 public static void storeStyleRanges(TableItem item, int column, StyleRange[] ranges) { 59 item.setData(STYLED_RANGES_KEY + column, ranges); 60 } 61 62 /** 63 * Returns the styled ranges which are stored in the given table item. 64 * 65 * @param item table item 66 * @param column the column index 67 * @return the styled ranges 68 */ getStyledRanges(TableItem item, int column)69 private static StyleRange[] getStyledRanges(TableItem item, int column) { 70 return (StyleRange[])item.getData(STYLED_RANGES_KEY + column); 71 } 72 TableOwnerDrawSupport(Table table)73 private TableOwnerDrawSupport(Table table) { 74 int orientation= table.getStyle() & (SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT); 75 fSharedLayout= new TextLayout(table.getDisplay()); 76 fSharedLayout.setOrientation(orientation); 77 } 78 79 @Override handleEvent(Event event)80 public void handleEvent(Event event) { 81 switch (event.type) { 82 case SWT.MeasureItem: 83 measureItem(event); 84 break; 85 case SWT.EraseItem: 86 event.detail &= ~SWT.FOREGROUND; 87 break; 88 case SWT.PaintItem: 89 performPaint(event); 90 break; 91 case SWT.Dispose: 92 widgetDisposed(); 93 break; 94 } 95 } 96 97 /** 98 * Handles the measure event. 99 * 100 * @param event the measure event 101 */ measureItem(Event event)102 private void measureItem(Event event) { 103 boolean isSelected= (event.detail & SWT.SELECTED) != 0; 104 fDeltaOfLastMeasure= updateTextLayout((TableItem) event.item, event.index, isSelected); 105 event.width+= fDeltaOfLastMeasure; 106 } 107 updateTextLayout(TableItem item, int index, boolean isSelected)108 private int updateTextLayout(TableItem item, int index, boolean isSelected) { 109 fSharedLayout.setFont(item.getFont(index)); 110 111 // XXX: needed to clear the style info, see https://bugs.eclipse.org/bugs/show_bug.cgi?id=226090 112 fSharedLayout.setText(""); //$NON-NLS-1$ 113 114 fSharedLayout.setText(item.getText(index)); 115 116 int originalTextWidth= fSharedLayout.getBounds().width; // text width without any styles 117 118 StyleRange[] ranges= getStyledRanges(item, index); 119 if (ranges != null) { 120 for (StyleRange range : ranges) { 121 StyleRange curr= range; 122 if (isSelected) { 123 curr= (StyleRange) curr.clone(); 124 curr.foreground= null; 125 curr.background= null; 126 } 127 fSharedLayout.setStyle(curr, curr.start, curr.start + curr.length - 1); 128 } 129 } 130 131 return fSharedLayout.getBounds().width - originalTextWidth; 132 } 133 134 /** 135 * Performs the paint operation. 136 * 137 * @param event the event 138 */ performPaint(Event event)139 private void performPaint(Event event) { 140 TableItem item= (TableItem) event.item; 141 GC gc= event.gc; 142 int index= event.index; 143 144 boolean isSelected= (event.detail & SWT.SELECTED) != 0; 145 146 // Remember colors to restore the GC later 147 Color oldForeground= gc.getForeground(); 148 Color oldBackground= gc.getBackground(); 149 150 if (!isSelected) { 151 Color foreground= item.getForeground(index); 152 gc.setForeground(foreground); 153 154 Color background= item.getBackground(index); 155 gc.setBackground(background); 156 } 157 158 Image image=item.getImage(index); 159 if (image != null) { 160 Rectangle imageBounds=item.getImageBounds(index); 161 Rectangle bounds=image.getBounds(); 162 int x=imageBounds.x + Math.max(0, (imageBounds.width - bounds.width) / 2); 163 int y=imageBounds.y + Math.max(0, (imageBounds.height - bounds.height) / 2); 164 gc.drawImage(image, x, y); 165 } 166 167 // fSharedLayout already configured in measureItem(Event) 168 Rectangle textBounds=item.getTextBounds(index); 169 if (textBounds != null) { 170 Rectangle layoutBounds=fSharedLayout.getBounds(); 171 int x=textBounds.x; 172 int y=textBounds.y + Math.max(0, (textBounds.height - layoutBounds.height) / 2); 173 fSharedLayout.draw(gc, x, y); 174 } 175 176 if ((event.detail & SWT.FOCUSED) != 0) { 177 Rectangle focusBounds=item.getBounds(); 178 gc.drawFocus(focusBounds.x, focusBounds.y, focusBounds.width + fDeltaOfLastMeasure, focusBounds.height); 179 } 180 181 if (!isSelected) { 182 gc.setForeground(oldForeground); 183 gc.setBackground(oldBackground); 184 } 185 } 186 widgetDisposed()187 private void widgetDisposed() { 188 fSharedLayout.dispose(); 189 } 190 } 191 192