1 /******************************************************************************* 2 * Copyright (c) 2003, 2015, 2019 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 * Fair Issac Corp - bug 287103 - NCSLabelProvider does not properly handle overrides 14 * Stefan Winkler <stefan@winklerweb.net> - bug 178019 - CNF Tooltip support 15 *******************************************************************************/ 16 package org.eclipse.ui.internal.navigator; 17 18 19 import java.util.Collection; 20 import java.util.Iterator; 21 22 import org.eclipse.core.commands.common.EventManager; 23 import org.eclipse.core.runtime.SafeRunner; 24 import org.eclipse.jface.util.SafeRunnable; 25 import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider.IStyledLabelProvider; 26 import org.eclipse.jface.viewers.IColorProvider; 27 import org.eclipse.jface.viewers.IFontProvider; 28 import org.eclipse.jface.viewers.ILabelProvider; 29 import org.eclipse.jface.viewers.ILabelProviderListener; 30 import org.eclipse.jface.viewers.ITableLabelProvider; 31 import org.eclipse.jface.viewers.IToolTipProvider; 32 import org.eclipse.jface.viewers.ITreePathLabelProvider; 33 import org.eclipse.jface.viewers.LabelProviderChangedEvent; 34 import org.eclipse.jface.viewers.StyledString; 35 import org.eclipse.jface.viewers.TreePath; 36 import org.eclipse.jface.viewers.ViewerLabel; 37 import org.eclipse.osgi.util.NLS; 38 import org.eclipse.swt.graphics.Color; 39 import org.eclipse.swt.graphics.Font; 40 import org.eclipse.swt.graphics.Image; 41 import org.eclipse.ui.internal.navigator.extensions.NavigatorContentExtension; 42 import org.eclipse.ui.navigator.CommonViewer; 43 import org.eclipse.ui.navigator.ICommonLabelProvider; 44 import org.eclipse.ui.navigator.INavigatorContentService; 45 46 /** 47 * <p> 48 * Provides relevant labels based on the associated 49 * {@link INavigatorContentService}for the contents of a 50 * TreeViewer. 51 * </p> 52 * 53 * <p> 54 * Except for the dependency on 55 * {@link INavigatorContentService}, this class has no 56 * dependencies on the rest of the Common Navigator framework. Tree viewers that would like to use 57 * the extensions defined by the Common Navigator, without using the actual view part or other 58 * pieces of functionality (filters, sorting, etc) may choose to use this class, in effect using an 59 * extensible label provider. 60 * </p> 61 * 62 * @since 3.2 63 * 64 * @see org.eclipse.ui.internal.navigator.NavigatorContentService 65 * @see org.eclipse.ui.internal.navigator.NavigatorContentServiceContentProvider 66 */ 67 public class NavigatorContentServiceLabelProvider extends EventManager 68 implements ILabelProvider, IColorProvider, IFontProvider, ITreePathLabelProvider, ITableLabelProvider, 69 ILabelProviderListener, IStyledLabelProvider, IToolTipProvider { 70 71 private final NavigatorContentService contentService; 72 private final boolean isContentServiceSelfManaged; 73 private final ReusableViewerLabel reusableLabel = new ReusableViewerLabel(); 74 75 76 /** 77 * <p> 78 * Uses the supplied content service to acquire the available extensions. 79 * </p> 80 * 81 * @param aContentService 82 * The associated NavigatorContentService that should be used to acquire information. 83 */ NavigatorContentServiceLabelProvider(NavigatorContentService aContentService)84 public NavigatorContentServiceLabelProvider(NavigatorContentService aContentService) { 85 contentService = aContentService; 86 isContentServiceSelfManaged = false; 87 } 88 89 /** 90 * <p> 91 * Return the appropriate image for anElement. The image will be used as the icon for anElement 92 * when displayed in the tree viewer. This method uses information from its contentService to 93 * know what extensions to use to supply the correct label. 94 * </p> 95 * {@inheritDoc} 96 * 97 * @param anElement 98 * An element from the Tree Viewer 99 * @return The Image that will be used as the icon when anElement is displayed in the viewer. 100 * @see org.eclipse.jface.viewers.ILabelProvider#getImage(java.lang.Object) 101 */ 102 @Override getImage(Object anElement)103 public Image getImage(Object anElement) { 104 return getColumnImage(anElement, -1); 105 } 106 107 @Override getColumnImage(Object element, int columnIndex)108 public Image getColumnImage(Object element, int columnIndex) { 109 Collection contentExtensions = contentService.findPossibleLabelExtensions(element); 110 Image image = null; 111 for (Iterator itr = contentExtensions.iterator(); itr.hasNext() && image == null; ) { 112 image = findImage((NavigatorContentExtension) itr.next(), element, columnIndex); 113 } 114 return image; 115 } 116 117 /** 118 * <p> 119 * Return a String representation of anElement to be used as the display name in the tree 120 * viewer. 121 * </p> 122 * 123 * @param anElement 124 * An element from the Tree Viewer 125 * @return The String label to display for the object when represented in the viewer. 126 * @see org.eclipse.jface.viewers.ILabelProvider#getText(java.lang.Object) 127 */ 128 @Override getText(Object anElement)129 public String getText(Object anElement) { 130 return getColumnText(anElement, -1); 131 } 132 133 @Override getColumnText(Object anElement, int aColumn)134 public String getColumnText(Object anElement, int aColumn) { 135 ILabelProvider[] labelProviders = contentService.findRelevantLabelProviders(anElement); 136 if (labelProviders.length == 0) 137 return NLS.bind(CommonNavigatorMessages.NavigatorContentServiceLabelProvider_Error_no_label_provider_for_0_, makeSmallString(anElement)); 138 String text = null; 139 for (ILabelProvider labelProvider : labelProviders) { 140 if (labelProvider instanceof ITableLabelProvider && aColumn != -1) 141 text = ((ITableLabelProvider)labelProvider).getColumnText(anElement, aColumn); 142 else 143 text = labelProvider.getText(anElement); 144 if (text != null && text.length() > 0) 145 return text; 146 } 147 return text; 148 } 149 150 @Override getStyledText(Object anElement)151 public StyledString getStyledText(Object anElement) { 152 Collection extensions = contentService.findPossibleLabelExtensions(anElement); 153 if (extensions.isEmpty()) 154 return new StyledString(NLS.bind(CommonNavigatorMessages.NavigatorContentServiceLabelProvider_Error_no_label_provider_for_0_, makeSmallString(anElement))); 155 156 StyledString text = null; 157 for (Iterator itr = extensions.iterator(); itr.hasNext() && text == null; ) { 158 text = findStyledText((NavigatorContentExtension) itr.next(), anElement); 159 } 160 return text != null ? text : new StyledString(); 161 } 162 163 /** 164 * Search for a styled text label and take overrides into account. 165 * Uses only simple ITreeContentProvider.getParent() style semantics. 166 * 167 * @return the styled text or <code>null</code> if no extension has been found that provides a label 168 */ findStyledText(NavigatorContentExtension foundExtension, Object anElement)169 private StyledString findStyledText(NavigatorContentExtension foundExtension, Object anElement) { 170 ICommonLabelProvider labelProvider= foundExtension.getLabelProvider(); 171 if (labelProvider instanceof IStyledLabelProvider) { 172 StyledString styledText= ((IStyledLabelProvider) labelProvider).getStyledText(anElement); 173 // paranoia check for null, although null is not a valid return value for IStyledLabelProvider.getStyledText 174 if (styledText != null && styledText.length() > 0) { 175 return styledText; 176 } 177 } else { 178 String text= labelProvider.getText(anElement); 179 if (text != null && text.length() > 0) { 180 return new StyledString(text); 181 } 182 } 183 return null; 184 } 185 makeSmallString(Object obj)186 private String makeSmallString(Object obj) { 187 if (obj == null) 188 return "null"; //$NON-NLS-1$ 189 String str = obj.toString(); 190 int len = str.length(); 191 return str.substring(0, len < 50 ? len : 49); 192 } 193 194 /** 195 * Search for image and take overrides into account. 196 * Uses only simple ITreeContentProvider.getParent() style semantics. 197 */ findImage(NavigatorContentExtension foundExtension, Object anElement, int aColumn)198 private Image findImage(NavigatorContentExtension foundExtension, Object anElement, int aColumn) { 199 Image image = null; 200 ICommonLabelProvider provider = foundExtension.getLabelProvider(); 201 if (provider instanceof ITableLabelProvider && aColumn >= 0) 202 image = ((ITableLabelProvider)provider).getColumnImage(anElement, aColumn); 203 else 204 image = provider.getImage(anElement); 205 206 return image; 207 } 208 209 @Override getFont(Object anElement)210 public Font getFont(Object anElement) { 211 ILabelProvider[] labelProviders = contentService.findRelevantLabelProviders(anElement); 212 for (ILabelProvider provider : labelProviders) { 213 if (provider instanceof IFontProvider) { 214 IFontProvider fontProvider = (IFontProvider) provider; 215 Font font = fontProvider.getFont(anElement); 216 if (font != null) { 217 return font; 218 } 219 } 220 } 221 return null; 222 } 223 224 @Override getForeground(Object anElement)225 public Color getForeground(Object anElement) { 226 ILabelProvider[] labelProviders = contentService.findRelevantLabelProviders(anElement); 227 for (ILabelProvider provider : labelProviders) { 228 if (provider instanceof IColorProvider) { 229 IColorProvider colorProvider = (IColorProvider) provider; 230 Color color = colorProvider.getForeground(anElement); 231 if (color != null) { 232 return color; 233 } 234 } 235 } 236 return null; 237 } 238 239 @Override getBackground(Object anElement)240 public Color getBackground(Object anElement) { 241 ILabelProvider[] labelProviders = contentService.findRelevantLabelProviders(anElement); 242 for (ILabelProvider provider : labelProviders) { 243 if (provider instanceof IColorProvider) { 244 IColorProvider colorProvider = (IColorProvider) provider; 245 Color color = colorProvider.getBackground(anElement); 246 if (color != null) { 247 return color; 248 } 249 } 250 } 251 return null; 252 } 253 254 @Override getToolTipText(Object anElement)255 public String getToolTipText(Object anElement) { 256 ILabelProvider[] labelProviders = contentService.findRelevantLabelProviders(anElement); 257 for (ILabelProvider provider : labelProviders) { 258 if (provider instanceof IToolTipProvider) { 259 IToolTipProvider tooltipProvider = (IToolTipProvider) provider; 260 String tooltip = tooltipProvider.getToolTipText(anElement); 261 if (tooltip != null && !tooltip.isEmpty()) { 262 return tooltip; 263 } 264 } 265 } 266 return null; 267 } 268 269 /** 270 * <p> 271 * Indicates whether anElelement has aProperty that affects the display of the label. 272 * </p> 273 * {@inheritDoc} 274 * 275 * @param anElement 276 * An element from the Tree Viewer 277 * @param aProperty 278 * A property of the given element that could be a label provider 279 * @return True if any of the extensions enabled on anElement consider aProperty a 280 * label-changing property. 281 * @see org.eclipse.jface.viewers.IBaseLabelProvider#isLabelProperty(java.lang.Object, 282 * java.lang.String) 283 */ 284 @Override isLabelProperty(Object anElement, String aProperty)285 public boolean isLabelProperty(Object anElement, String aProperty) { 286 boolean result = false; 287 ILabelProvider[] labelProviders = contentService.findRelevantLabelProviders(anElement); 288 for (int i = 0; i < labelProviders.length && !result; i++) { 289 result = labelProviders[i].isLabelProperty(anElement, aProperty); 290 } 291 return result; 292 } 293 294 295 /** 296 * <p> 297 * Label provider listeners are currently supported. 298 * </p> 299 * 300 * {@inheritDoc} 301 * @see org.eclipse.jface.viewers.IBaseLabelProvider#addListener(org.eclipse.jface.viewers.ILabelProviderListener) 302 */ 303 @Override addListener(ILabelProviderListener aListener)304 public void addListener(ILabelProviderListener aListener) { 305 addListenerObject(aListener); 306 } 307 308 /** 309 * <p> 310 * Label provider listeners are currently supported. 311 * </p> 312 * 313 * {@inheritDoc} 314 * @see org.eclipse.jface.viewers.IBaseLabelProvider#removeListener(org.eclipse.jface.viewers.ILabelProviderListener) 315 */ 316 @Override removeListener(ILabelProviderListener aListener)317 public void removeListener(ILabelProviderListener aListener) { 318 removeListenerObject(aListener); 319 } 320 321 /** 322 * <p> 323 * Dispose of the content service, if it was created and not supplied. 324 * </p> 325 * <p> 326 * <b>If a client uses this class outside of the framework of {@link CommonViewer}, this method 327 * must be called when finished. </b> 328 * </p> 329 * 330 * {@inheritDoc} 331 * @see org.eclipse.jface.viewers.IBaseLabelProvider#dispose() 332 */ 333 @Override dispose()334 public void dispose() { 335 if (isContentServiceSelfManaged) { 336 contentService.dispose(); 337 } 338 339 } 340 341 /** 342 * Fires a label provider changed event to all registered listeners 343 * Only listeners registered at the time this method is called are notified. 344 * 345 * @param event a label provider changed event 346 * 347 * @see ILabelProviderListener#labelProviderChanged 348 */ fireLabelProviderChanged( final LabelProviderChangedEvent event)349 protected void fireLabelProviderChanged( 350 final LabelProviderChangedEvent event) { 351 for (Object listener : getListeners()) { 352 final ILabelProviderListener labelProviderListener = (ILabelProviderListener) listener; 353 SafeRunner.run(new SafeRunnable() { 354 @Override 355 public void run() { 356 labelProviderListener.labelProviderChanged(event); 357 } 358 }); 359 360 } 361 } 362 363 @Override updateLabel(ViewerLabel label, TreePath elementPath)364 public void updateLabel(ViewerLabel label, TreePath elementPath) { 365 366 Collection contentExtensions = contentService.findPossibleLabelExtensions(elementPath.getLastSegment()); 367 reusableLabel.reset(label); 368 for (Iterator itr = contentExtensions.iterator(); itr.hasNext() && !(reusableLabel.isValid() && reusableLabel.hasChanged()); ) { 369 findUpdateLabel((NavigatorContentExtension)itr.next(), reusableLabel, elementPath); 370 } 371 reusableLabel.fill(label); 372 } 373 374 375 /** 376 * Search for text label and take overrides into account. 377 * Uses only simple ITreeContentProvider.getParent() style semantics. 378 */ findUpdateLabel(NavigatorContentExtension foundExtension, ReusableViewerLabel label, TreePath elementPath)379 private void findUpdateLabel(NavigatorContentExtension foundExtension, ReusableViewerLabel label, TreePath elementPath) { 380 381 ILabelProvider labelProvider = foundExtension.getLabelProvider(); 382 if (labelProvider instanceof ITreePathLabelProvider) { 383 ITreePathLabelProvider tplp = (ITreePathLabelProvider) labelProvider; 384 tplp.updateLabel(label, elementPath); 385 } else { 386 label.setImage(labelProvider.getImage(elementPath.getLastSegment())); 387 label.setText(labelProvider.getText(elementPath.getLastSegment())); 388 } 389 } 390 391 @Override labelProviderChanged(LabelProviderChangedEvent event)392 public void labelProviderChanged(LabelProviderChangedEvent event) { 393 fireLabelProviderChanged(event); 394 } 395 396 397 398 } 399