Added
Link Here
|
1 |
/******************************************************************************* |
2 |
* Copyright (c) 2007 IBM Corporation and others. |
3 |
* All rights reserved. This program and the accompanying materials |
4 |
* are made available under the terms of the Eclipse Public License v1.0 |
5 |
* which accompanies this distribution, and is available at |
6 |
* http://www.eclipse.org/legal/epl-v10.html |
7 |
* |
8 |
* Contributors: |
9 |
* IBM Corporation - initial API and implementation |
10 |
*******************************************************************************/ |
11 |
package org.eclipse.jface.viewers; |
12 |
|
13 |
import org.eclipse.core.runtime.Assert; |
14 |
import org.eclipse.jface.window.Window; |
15 |
import org.eclipse.swt.SWT; |
16 |
import org.eclipse.swt.custom.StyleRange; |
17 |
import org.eclipse.swt.graphics.Color; |
18 |
import org.eclipse.swt.graphics.Font; |
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.Display; |
24 |
import org.eclipse.swt.widgets.Event; |
25 |
import org.eclipse.swt.widgets.Item; |
26 |
import org.eclipse.swt.widgets.TableItem; |
27 |
import org.eclipse.swt.widgets.TreeItem; |
28 |
|
29 |
/** |
30 |
* A {@link SimpleStyledCellLabelProvider} supports styled labels by using owner |
31 |
* draw by preserving native viewer behavior: |
32 |
* <ul> |
33 |
* <li>similar image and label positioning</li> |
34 |
* <li>native drawing of focus and selection</li> |
35 |
* </ul> |
36 |
* |
37 |
* |
38 |
* <p> |
39 |
* For providing the label's styles, create a subclass and overwrite |
40 |
* {@link SimpleStyledCellLabelProvider#getLabelPresentationInfo(Object)} to |
41 |
* return all information needed to render a element. |
42 |
* </p> |
43 |
* <p> |
44 |
* The {@link SimpleStyledCellLabelProvider} will ignore all font settings on |
45 |
* {@link StyleRange}. Different fonts would make labels wider, and the native |
46 |
* selection drawing could not be reused. |
47 |
* </p> |
48 |
* |
49 |
* <p> |
50 |
* To set up styled labels for your viewer's control, you must call |
51 |
* {@link OwnerDrawLabelProvider#setUpOwnerDraw(ColumnViewer)}. |
52 |
* </p> |
53 |
* |
54 |
* <p><strong>NOTE:</strong> This API is experimental and may be deleted or |
55 |
* changed before 3.4 is released.</p> |
56 |
* |
57 |
* @since 3.4 |
58 |
*/ |
59 |
public abstract class SimpleStyledCellLabelProvider extends |
60 |
OwnerDrawLabelProvider { |
61 |
|
62 |
/** |
63 |
* Holds all information used to render a styled element. |
64 |
*/ |
65 |
public static class LabelPresentationInfo { |
66 |
|
67 |
private final String text; |
68 |
private final Image image; |
69 |
private final StyleRange[] ranges; |
70 |
|
71 |
private final Font defaultFont; |
72 |
private final Color defaultForegroundColor; |
73 |
private final Color defaultBackgroundColor; |
74 |
|
75 |
/** |
76 |
* Creates a {@link SimpleStyledCellLabelProvider.LabelPresentationInfo}. |
77 |
* |
78 |
* @param text |
79 |
* the text of the current element |
80 |
* @param ranges |
81 |
* the styled ranges for the element |
82 |
* @param image |
83 |
* the image for the element or <code>null</code> |
84 |
* @param defaultFont |
85 |
* the default font for the element or <code>null</code> |
86 |
* @param defaultForegroundColor |
87 |
* the default foreground color for the element or |
88 |
* <code>null</code> |
89 |
* @param defaultBackgroundColor |
90 |
* the default background color for the element or |
91 |
* <code>null</code> |
92 |
*/ |
93 |
public LabelPresentationInfo(String text, StyleRange[] ranges, |
94 |
Image image, Font defaultFont, Color defaultForegroundColor, |
95 |
Color defaultBackgroundColor) { |
96 |
Assert.isNotNull(text); |
97 |
Assert.isNotNull(ranges); |
98 |
this.text = text; |
99 |
this.ranges = ranges; |
100 |
this.image = image; |
101 |
this.defaultFont = defaultFont; |
102 |
this.defaultForegroundColor = defaultForegroundColor; |
103 |
this.defaultBackgroundColor = defaultBackgroundColor; |
104 |
} |
105 |
|
106 |
/** |
107 |
* Provides the text of the current element. |
108 |
* |
109 |
* @return returns the text. |
110 |
*/ |
111 |
public String getText() { |
112 |
return this.text; |
113 |
} |
114 |
|
115 |
/** |
116 |
* Provides the styled ranges that can be applied to the text provided |
117 |
* by {@link #getText()}. The {@link SimpleStyledCellLabelProvider} |
118 |
* will ignore all font settings. |
119 |
* |
120 |
* @return the styled ranges for the element |
121 |
*/ |
122 |
public StyleRange[] getStyleRanges() { |
123 |
return this.ranges; |
124 |
} |
125 |
|
126 |
/** |
127 |
* Provides the image of the current element. |
128 |
* |
129 |
* @return returns the image. |
130 |
*/ |
131 |
public Image getImage() { |
132 |
return this.image; |
133 |
} |
134 |
|
135 |
/** |
136 |
* Provides a default background color of the current element, which is |
137 |
* used for the part of the label where no background color is specified |
138 |
* in the StyleRanges provided by {@link #getStyleRanges}. |
139 |
* |
140 |
* @return the background color for the element, or <code>null</code> |
141 |
* to use the default background color |
142 |
*/ |
143 |
public Color getDefaultBackground() { |
144 |
return this.defaultBackgroundColor; |
145 |
} |
146 |
|
147 |
/** |
148 |
* Provides a default font of the current element. |
149 |
* |
150 |
* @return the font for the element, or <code>null</code> to use the |
151 |
* default font |
152 |
*/ |
153 |
public Font getDefaultFont() { |
154 |
return this.defaultFont; |
155 |
} |
156 |
|
157 |
/** |
158 |
* Provides a default foreground color of the current element, which is |
159 |
* used for the part of the label where no foreground color is specified |
160 |
* in the StyleRanges provided by {@link #getStyleRanges}. |
161 |
* |
162 |
* @return the foreground color for the element, or <code>null</code> |
163 |
* to use the default foreground color |
164 |
*/ |
165 |
public Color getDefaultForeground() { |
166 |
return this.defaultForegroundColor; |
167 |
} |
168 |
|
169 |
} |
170 |
|
171 |
private static final String KEY_TEXT_LAYOUT = "styled_label_key_"; //$NON-NLS-1$ |
172 |
|
173 |
/** |
174 |
* Style constant for indicating that the styled colors are to be applied |
175 |
* even it the viewer's item is selected. Default is not to apply colors. |
176 |
*/ |
177 |
public static final int COLORS_ON_SELECTION = 1 << 0; |
178 |
|
179 |
/** |
180 |
* Style constant for indicating to draw the focus if requested by the owner |
181 |
* draw event. Default is to draw the focus. |
182 |
*/ |
183 |
public static final int NO_FOCUS = 1 << 1; |
184 |
|
185 |
private final int style; |
186 |
|
187 |
private TextLayout cachedTextLayout; // reused text layout for |
188 |
// 'cachedLabelInfo' |
189 |
private LabelPresentationInfo cachedLabelInfo; |
190 |
private boolean cachedWasWithColors; |
191 |
|
192 |
/** |
193 |
* Creates a new StyledCellLabelProvider. The label provider does not apply |
194 |
* colors on selection. |
195 |
*/ |
196 |
public SimpleStyledCellLabelProvider() { |
197 |
this(0); |
198 |
} |
199 |
|
200 |
/** |
201 |
* Creates a new StyledCellLabelProvider. |
202 |
* |
203 |
* @param style |
204 |
* the style bits |
205 |
* @see SimpleStyledCellLabelProvider#COLORS_ON_SELECTION |
206 |
* @see SimpleStyledCellLabelProvider#NO_FOCUS |
207 |
*/ |
208 |
public SimpleStyledCellLabelProvider(int style) { |
209 |
this.style = style; |
210 |
} |
211 |
|
212 |
/** |
213 |
* Returns a {@link LabelPresentationInfo} instance containing the text, |
214 |
* image and style information to use for displaying element. |
215 |
* |
216 |
* @param element |
217 |
* the element to create a presentation info for |
218 |
* @return the presentation info |
219 |
*/ |
220 |
protected abstract LabelPresentationInfo getLabelPresentationInfo( |
221 |
Object element); |
222 |
|
223 |
/* |
224 |
* (non-Javadoc) |
225 |
* |
226 |
* @see org.eclipse.jface.viewers.BaseLabelProvider#dispose() |
227 |
*/ |
228 |
public void dispose() { |
229 |
if (this.cachedTextLayout != null) { |
230 |
cachedTextLayout.dispose(); |
231 |
cachedTextLayout = null; |
232 |
} |
233 |
cachedLabelInfo = null; |
234 |
} |
235 |
|
236 |
/* |
237 |
* (non-Javadoc) |
238 |
* |
239 |
* @see org.eclipse.jface.viewers.OwnerDrawLabelProvider#update(org.eclipse.jface.viewers.ViewerCell) |
240 |
*/ |
241 |
public void update(ViewerCell cell) { |
242 |
LabelPresentationInfo info = getLabelPresentationInfo(cell.getElement()); |
243 |
cell.getItem().setData(KEY_TEXT_LAYOUT + cell.getColumnIndex(), info); // store it in the item |
244 |
// to avoid |
245 |
// recomputation |
246 |
|
247 |
cell.setImage(info.getImage()); // seems to be necessary so that |
248 |
// item.getText/ImageBounds work |
249 |
cell.setText(info.getText()); |
250 |
cell.setFont(info.getDefaultFont()); |
251 |
|
252 |
super.update(cell); |
253 |
} |
254 |
|
255 |
private TextLayout getSharedTextLayout(Display display) { |
256 |
if (cachedTextLayout == null) { |
257 |
cachedTextLayout = new TextLayout(display); |
258 |
cachedTextLayout.setOrientation(Window.getDefaultOrientation()); |
259 |
} |
260 |
return cachedTextLayout; |
261 |
} |
262 |
|
263 |
private boolean useColors(Event event) { |
264 |
return (event.detail & SWT.SELECTED) == 0 |
265 |
|| (this.style & COLORS_ON_SELECTION) != 0; |
266 |
} |
267 |
|
268 |
private boolean drawFocus(Event event) { |
269 |
return (event.detail & SWT.FOCUSED) != 0 |
270 |
&& (this.style & NO_FOCUS) == 0; |
271 |
} |
272 |
|
273 |
/** |
274 |
* Returns a {@link LabelPresentationInfo} instance for the given event. |
275 |
* |
276 |
* @param event |
277 |
* the measure or paint event for which a TextLayout is needed |
278 |
* @param element |
279 |
* the model element |
280 |
* @return a TextLayout instance |
281 |
*/ |
282 |
private LabelPresentationInfo getLabelPresentationInfo(Event event, |
283 |
Object element) { |
284 |
|
285 |
// cache the label info in the data as owner draw labels are requested |
286 |
// in a high rate |
287 |
LabelPresentationInfo labelInfo = (LabelPresentationInfo) event.item |
288 |
.getData(KEY_TEXT_LAYOUT + event.index); |
289 |
if (labelInfo == null) { |
290 |
labelInfo = getLabelPresentationInfo(element); |
291 |
event.item.setData(KEY_TEXT_LAYOUT + event.index, labelInfo); |
292 |
} |
293 |
return labelInfo; |
294 |
} |
295 |
|
296 |
/** |
297 |
* Returns a {@link TextLayout} instance for the given |
298 |
* {@link LabelPresentationInfo}. The text layout instance is managed by |
299 |
* the label provider. Caller of the method must not dispose the text |
300 |
* layout. |
301 |
* |
302 |
* @param diplay |
303 |
* the current display |
304 |
* @param labelPresentation |
305 |
* the viewerLabel the label info |
306 |
* |
307 |
* @param applyColors |
308 |
* if set, create colors in the result |
309 |
* @param element |
310 |
* the model element |
311 |
* @return a TextLayout instance |
312 |
*/ |
313 |
private TextLayout getTextLayoutForInfo(Display display, |
314 |
LabelPresentationInfo labelPresentation, boolean applyColors) { |
315 |
// can use cache? |
316 |
if (cachedLabelInfo == labelPresentation |
317 |
&& applyColors == cachedWasWithColors) { |
318 |
return cachedTextLayout; // use cached layout |
319 |
} |
320 |
|
321 |
TextLayout sharedLayout = getSharedTextLayout(display); |
322 |
applyInfoToLayout(sharedLayout, labelPresentation, applyColors); |
323 |
|
324 |
cachedLabelInfo = labelPresentation; |
325 |
cachedWasWithColors = applyColors; |
326 |
|
327 |
return sharedLayout; |
328 |
} |
329 |
|
330 |
/** |
331 |
* Fills the given text layout with the styles, text and font of the label |
332 |
* info. |
333 |
* |
334 |
* @param layout |
335 |
* the text layout to fill |
336 |
* @param labelInfo |
337 |
* the viewer label |
338 |
* @param applyColors |
339 |
* is set, colors will be used |
340 |
*/ |
341 |
private void applyInfoToLayout(TextLayout layout, |
342 |
LabelPresentationInfo labelInfo, boolean applyColors) { |
343 |
layout.setText(""); // make sure no previous ranges are kept //$NON-NLS-1$ |
344 |
layout.setText(labelInfo.getText()); |
345 |
layout.setFont(labelInfo.getDefaultFont()); // set also if null to clear |
346 |
// previous usages |
347 |
|
348 |
StyleRange[] styleRanges = labelInfo.getStyleRanges(); |
349 |
|
350 |
for (int i = 0; i < styleRanges.length; i++) { |
351 |
StyleRange curr = styleRanges[i]; |
352 |
|
353 |
// if no colors apply or font is set, create a clone and clear the |
354 |
// colors and font |
355 |
if (curr.font != null || !applyColors |
356 |
&& (curr.foreground != null || curr.background != null)) { |
357 |
curr = (StyleRange) curr.clone(); |
358 |
curr.font = null; |
359 |
if (!applyColors) { |
360 |
curr.foreground = null; |
361 |
curr.background = null; |
362 |
} |
363 |
} |
364 |
layout.setStyle(curr, curr.start, curr.start + curr.length - 1); |
365 |
} |
366 |
} |
367 |
|
368 |
/** |
369 |
* Handle the erase event. The default implementation does nothing to ensure |
370 |
* keep native selection highlighting working. |
371 |
* |
372 |
* @param event |
373 |
* the erase event |
374 |
* @param element |
375 |
* the model object |
376 |
* @see SWT#EraseItem |
377 |
*/ |
378 |
protected void erase(Event event, Object element) { |
379 |
event.detail &= ~SWT.FOREGROUND; |
380 |
} |
381 |
|
382 |
/* |
383 |
* (non-Javadoc) |
384 |
* |
385 |
* @see org.eclipse.jface.viewers.OwnerDrawLabelProvider#measure(org.eclipse.swt.widgets.Event, |
386 |
* java.lang.Object) |
387 |
*/ |
388 |
protected void measure(Event event, Object element) { |
389 |
// use native measuring |
390 |
} |
391 |
|
392 |
/* |
393 |
* (non-Javadoc) |
394 |
* |
395 |
* @see org.eclipse.jface.viewers.OwnerDrawLabelProvider#paint(org.eclipse.swt.widgets.Event, |
396 |
* java.lang.Object) |
397 |
*/ |
398 |
protected void paint(Event event, Object element) { |
399 |
LabelPresentationInfo labelInfo = getLabelPresentationInfo(event, |
400 |
element); |
401 |
|
402 |
boolean applyColors = useColors(event); |
403 |
GC gc = event.gc; |
404 |
Color oldForeground = gc.getForeground(); // remember colors to |
405 |
// restore the GC later |
406 |
Color oldBackground = gc.getBackground(); |
407 |
|
408 |
if (applyColors) { |
409 |
Color foreground = labelInfo.getDefaultForeground(); |
410 |
if (foreground != null) { |
411 |
gc.setForeground(foreground); |
412 |
} |
413 |
Color background = labelInfo.getDefaultBackground(); |
414 |
if (background != null) { |
415 |
gc.setBackground(background); |
416 |
} |
417 |
} |
418 |
|
419 |
Image image = labelInfo.getImage(); |
420 |
if (image != null) { |
421 |
Rectangle imageBounds = getImageBounds(event); |
422 |
Rectangle bounds = image.getBounds(); |
423 |
|
424 |
// center the image in the given space |
425 |
int x = imageBounds.x |
426 |
+ Math.max(0, (imageBounds.width - bounds.width) / 2); |
427 |
int y = imageBounds.y |
428 |
+ Math.max(0, (imageBounds.height - bounds.height) / 2); |
429 |
gc.drawImage(image, x, y); |
430 |
} |
431 |
|
432 |
TextLayout textLayout = getTextLayoutForInfo(event.display, labelInfo, |
433 |
applyColors); |
434 |
|
435 |
Rectangle layoutBounds = textLayout.getBounds(); |
436 |
Rectangle textBounds = getTextBounds(event); |
437 |
|
438 |
int x = textBounds.x; |
439 |
int y = textBounds.y |
440 |
+ Math.max(0, (textBounds.height - layoutBounds.height) / 2); |
441 |
|
442 |
textLayout.draw(gc, x, y); |
443 |
|
444 |
if (drawFocus(event)) { |
445 |
Rectangle focusBounds = getBounds(event); |
446 |
gc.drawFocus(focusBounds.x, focusBounds.y, focusBounds.width, |
447 |
focusBounds.height); |
448 |
} |
449 |
|
450 |
gc.setForeground(oldForeground); |
451 |
gc.setBackground(oldBackground); |
452 |
} |
453 |
|
454 |
private Rectangle getBounds(Event event) { |
455 |
Item item = (Item) event.item; |
456 |
if (item instanceof TreeItem) { |
457 |
return ((TreeItem) item).getBounds(); |
458 |
} else if (item instanceof TableItem) { |
459 |
return ((TableItem) item).getBounds(); |
460 |
} |
461 |
return null; |
462 |
} |
463 |
|
464 |
private Rectangle getImageBounds(Event event) { |
465 |
Item item = (Item) event.item; |
466 |
if (item instanceof TreeItem) { |
467 |
return ((TreeItem) item).getImageBounds(event.index); |
468 |
} else if (item instanceof TableItem) { |
469 |
return ((TableItem) item).getImageBounds(event.index); |
470 |
} |
471 |
return null; |
472 |
} |
473 |
|
474 |
private Rectangle getTextBounds(Event event) { |
475 |
Item item = (Item) event.item; |
476 |
if (item instanceof TreeItem) { |
477 |
return ((TreeItem) item).getTextBounds(event.index); |
478 |
} else if (item instanceof TableItem) { |
479 |
return ((TableItem) item).getTextBounds(event.index); |
480 |
} |
481 |
return null; |
482 |
} |
483 |
|
484 |
} |