1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Eclipse Public License, Version 1.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.eclipse.org/org/documents/epl-v10.php 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.ide.eclipse.adt.internal.preferences; 17 18 import static com.android.tools.lint.detector.api.TextFormat.RAW; 19 import static com.android.tools.lint.detector.api.TextFormat.TEXT; 20 21 import com.android.annotations.NonNull; 22 import com.android.ide.eclipse.adt.AdtPlugin; 23 import com.android.ide.eclipse.adt.AdtUtils; 24 import com.android.ide.eclipse.adt.internal.lint.EclipseLintClient; 25 import com.android.ide.eclipse.adt.internal.lint.EclipseLintRunner; 26 import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper; 27 import com.android.tools.lint.client.api.Configuration; 28 import com.android.tools.lint.client.api.IssueRegistry; 29 import com.android.tools.lint.detector.api.Category; 30 import com.android.tools.lint.detector.api.Issue; 31 import com.android.tools.lint.detector.api.Project; 32 import com.android.tools.lint.detector.api.Severity; 33 import com.android.tools.lint.detector.api.TextFormat; 34 35 import org.eclipse.core.resources.IProject; 36 import org.eclipse.core.resources.IWorkspace; 37 import org.eclipse.core.resources.ResourcesPlugin; 38 import org.eclipse.core.runtime.IAdaptable; 39 import org.eclipse.jface.dialogs.MessageDialog; 40 import org.eclipse.jface.viewers.IColorProvider; 41 import org.eclipse.jface.viewers.ILabelProviderListener; 42 import org.eclipse.jface.viewers.ITableLabelProvider; 43 import org.eclipse.jface.viewers.TreeNodeContentProvider; 44 import org.eclipse.jface.viewers.TreeViewer; 45 import org.eclipse.jface.viewers.TreeViewerColumn; 46 import org.eclipse.jface.viewers.Viewer; 47 import org.eclipse.jface.window.Window; 48 import org.eclipse.swt.SWT; 49 import org.eclipse.swt.events.ControlEvent; 50 import org.eclipse.swt.events.ControlListener; 51 import org.eclipse.swt.events.ModifyEvent; 52 import org.eclipse.swt.events.ModifyListener; 53 import org.eclipse.swt.events.SelectionEvent; 54 import org.eclipse.swt.events.SelectionListener; 55 import org.eclipse.swt.events.TraverseEvent; 56 import org.eclipse.swt.events.TraverseListener; 57 import org.eclipse.swt.graphics.Color; 58 import org.eclipse.swt.graphics.Image; 59 import org.eclipse.swt.graphics.Rectangle; 60 import org.eclipse.swt.layout.GridData; 61 import org.eclipse.swt.layout.GridLayout; 62 import org.eclipse.swt.widgets.Button; 63 import org.eclipse.swt.widgets.Combo; 64 import org.eclipse.swt.widgets.Composite; 65 import org.eclipse.swt.widgets.Control; 66 import org.eclipse.swt.widgets.Label; 67 import org.eclipse.swt.widgets.Link; 68 import org.eclipse.swt.widgets.Shell; 69 import org.eclipse.swt.widgets.Text; 70 import org.eclipse.swt.widgets.Tree; 71 import org.eclipse.swt.widgets.TreeColumn; 72 import org.eclipse.swt.widgets.TreeItem; 73 import org.eclipse.ui.ISharedImages; 74 import org.eclipse.ui.IWorkbench; 75 import org.eclipse.ui.IWorkbenchPreferencePage; 76 import org.eclipse.ui.PlatformUI; 77 import org.eclipse.ui.dialogs.PreferencesUtil; 78 import org.eclipse.ui.dialogs.PropertyPage; 79 80 import java.io.File; 81 import java.util.ArrayList; 82 import java.util.Collections; 83 import java.util.HashMap; 84 import java.util.List; 85 import java.util.Locale; 86 import java.util.Map; 87 88 /** Preference page for configuring Lint preferences */ 89 public class LintPreferencePage extends PropertyPage implements IWorkbenchPreferencePage, 90 SelectionListener, ControlListener, ModifyListener { 91 private static final String ID = 92 "com.android.ide.eclipse.common.preferences.LintPreferencePage"; //$NON-NLS-1$ 93 private static final int ID_COLUMN_WIDTH = 150; 94 95 private EclipseLintClient mClient; 96 private IssueRegistry mRegistry; 97 private Configuration mConfiguration; 98 private IProject mProject; 99 private Map<Issue, Severity> mSeverities = new HashMap<Issue, Severity>(); 100 private Map<Issue, Severity> mInitialSeverities = Collections.<Issue, Severity>emptyMap(); 101 private boolean mIgnoreEvent; 102 103 private Tree mTree; 104 private TreeViewer mTreeViewer; 105 private Text mDetailsText; 106 private Button mCheckFileCheckbox; 107 private Button mCheckExportCheckbox; 108 private Link mWorkspaceLink; 109 private TreeColumn mNameColumn; 110 private TreeColumn mIdColumn; 111 private Combo mSeverityCombo; 112 private Button mIncludeAll; 113 private Button mIgnoreAll; 114 private Text mSearch; 115 116 /** 117 * Create the preference page. 118 */ 119 public LintPreferencePage() { 120 setPreferenceStore(AdtPlugin.getDefault().getPreferenceStore()); 121 } 122 123 @Override 124 public Control createContents(Composite parent) { 125 IAdaptable resource = getElement(); 126 if (resource != null) { 127 mProject = (IProject) resource.getAdapter(IProject.class); 128 } 129 130 Composite container = new Composite(parent, SWT.NULL); 131 container.setLayout(new GridLayout(2, false)); 132 133 if (mProject != null) { 134 Label projectLabel = new Label(container, SWT.CHECK); 135 projectLabel.setLayoutData(new GridData(SWT.LEFT, SWT.BOTTOM, false, false, 1, 136 1)); 137 projectLabel.setText("Project-specific configuration:"); 138 139 mWorkspaceLink = new Link(container, SWT.NONE); 140 mWorkspaceLink.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); 141 mWorkspaceLink.setText("<a>Configure Workspace Settings...</a>"); 142 mWorkspaceLink.addSelectionListener(this); 143 } else { 144 mCheckFileCheckbox = new Button(container, SWT.CHECK); 145 mCheckFileCheckbox.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 146 2, 1)); 147 mCheckFileCheckbox.setSelection(true); 148 mCheckFileCheckbox.setText("When saving files, check for errors"); 149 150 mCheckExportCheckbox = new Button(container, SWT.CHECK); 151 mCheckExportCheckbox.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 152 2, 1)); 153 mCheckExportCheckbox.setSelection(true); 154 mCheckExportCheckbox.setText("Run full error check when exporting app and abort if fatal errors are found"); 155 156 Label separator = new Label(container, SWT.SEPARATOR | SWT.HORIZONTAL); 157 separator.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 2, 1)); 158 159 Label checkListLabel = new Label(container, SWT.NONE); 160 checkListLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1)); 161 checkListLabel.setText("Issues:"); 162 } 163 164 mRegistry = EclipseLintClient.getRegistry(); 165 mClient = new EclipseLintClient(mRegistry, 166 mProject != null ? Collections.singletonList(mProject) : null, null, false); 167 Project project = null; 168 if (mProject != null) { 169 File dir = AdtUtils.getAbsolutePath(mProject).toFile(); 170 project = mClient.getProject(dir, dir); 171 } 172 mConfiguration = mClient.getConfigurationFor(project); 173 174 mSearch = new Text(container, SWT.SEARCH | SWT.ICON_CANCEL | SWT.ICON_SEARCH); 175 mSearch.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1)); 176 mSearch.addSelectionListener(this); 177 mSearch.addModifyListener(this); 178 // Grab the Enter key such that pressing return in the search box filters (instead 179 // of closing the options dialog) 180 mSearch.setMessage("type filter text (use ~ to filter by severity, e.g. ~ignore)"); 181 mSearch.addTraverseListener(new TraverseListener() { 182 @Override 183 public void keyTraversed(TraverseEvent e) { 184 if (e.keyCode == SWT.CR) { 185 updateFilter(); 186 e.doit = false; 187 } else if (e.keyCode == SWT.ARROW_DOWN) { 188 // Allow moving from the search into the table 189 if (mTree.getItemCount() > 0) { 190 TreeItem firstCategory = mTree.getItem(0); 191 if (firstCategory.getItemCount() > 0) { 192 TreeItem first = firstCategory.getItem(0); 193 mTree.setFocus(); 194 mTree.select(first); 195 } 196 } 197 } 198 } 199 }); 200 201 mTreeViewer = new TreeViewer(container, SWT.BORDER | SWT.FULL_SELECTION); 202 mTree = mTreeViewer.getTree(); 203 GridData gdTable = new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1); 204 gdTable.widthHint = 500; 205 gdTable.heightHint = 160; 206 mTree.setLayoutData(gdTable); 207 mTree.setLinesVisible(true); 208 mTree.setHeaderVisible(true); 209 210 TreeViewerColumn column1 = new TreeViewerColumn(mTreeViewer, SWT.NONE); 211 mIdColumn = column1.getColumn(); 212 mIdColumn.setWidth(100); 213 mIdColumn.setText("Id"); 214 215 TreeViewerColumn column2 = new TreeViewerColumn(mTreeViewer, SWT.FILL); 216 mNameColumn = column2.getColumn(); 217 mNameColumn.setWidth(100); 218 mNameColumn.setText("Name"); 219 220 mTreeViewer.setContentProvider(new ContentProvider()); 221 mTreeViewer.setLabelProvider(new LabelProvider()); 222 223 mDetailsText = new Text(container, SWT.BORDER | SWT.READ_ONLY | SWT.WRAP |SWT.V_SCROLL 224 | SWT.MULTI); 225 GridData gdText = new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 2); 226 gdText.heightHint = 80; 227 mDetailsText.setLayoutData(gdText); 228 229 Label severityLabel = new Label(container, SWT.NONE); 230 severityLabel.setText("Severity:"); 231 232 mSeverityCombo = new Combo(container, SWT.READ_ONLY); 233 mSeverityCombo.setItems(new String[] { 234 "(Default)", "Fatal", "Error", "Warning", "Information", "Ignore" 235 }); 236 GridData gdSeverityCombo = new GridData(SWT.FILL, SWT.TOP, false, false, 1, 1); 237 gdSeverityCombo.widthHint = 90; 238 mSeverityCombo.setLayoutData(gdSeverityCombo); 239 mSeverityCombo.setText(""); 240 mSeverityCombo.addSelectionListener(this); 241 242 List<Issue> issues = mRegistry.getIssues(); 243 for (Issue issue : issues) { 244 Severity severity = mConfiguration.getSeverity(issue); 245 mSeverities.put(issue, severity); 246 } 247 mInitialSeverities = new HashMap<Issue, Severity>(mSeverities); 248 249 mTreeViewer.setInput(mRegistry); 250 251 mTree.addSelectionListener(this); 252 // Add a listener to resize the column to the full width of the table 253 mTree.addControlListener(this); 254 255 loadSettings(false); 256 257 mTreeViewer.expandAll(); 258 259 return container; 260 } 261 262 /** 263 * Initialize the preference page. 264 */ 265 @Override 266 public void init(IWorkbench workbench) { 267 // Initialize the preference page 268 } 269 270 @Override 271 protected void contributeButtons(Composite parent) { 272 super.contributeButtons(parent); 273 274 // Add "Include All" button for quickly enabling all the detectors, including 275 // those disabled by default 276 mIncludeAll = new Button(parent, SWT.PUSH); 277 mIncludeAll.setText("Include All"); 278 mIncludeAll.addSelectionListener(this); 279 280 // Add "Ignore All" button for quickly disabling all the detectors 281 mIgnoreAll = new Button(parent, SWT.PUSH); 282 mIgnoreAll.setText("Ignore All"); 283 mIgnoreAll.addSelectionListener(this); 284 285 // As per the contributeButton javadoc: increase parent's column count for each 286 // added button 287 ((GridLayout) parent.getLayout()).numColumns += 2; 288 } 289 290 @Override 291 public void dispose() { 292 super.dispose(); 293 cancelPendingSearch(); 294 } 295 296 @Override 297 protected void performDefaults() { 298 super.performDefaults(); 299 300 mConfiguration.startBulkEditing(); 301 302 List<Issue> issues = mRegistry.getIssues(); 303 for (Issue issue : issues) { 304 mConfiguration.setSeverity(issue, null); 305 } 306 307 mConfiguration.finishBulkEditing(); 308 309 loadSettings(true); 310 } 311 312 @Override 313 public boolean performOk() { 314 storeSettings(); 315 return true; 316 } 317 318 private void loadSettings(boolean refresh) { 319 if (mCheckExportCheckbox != null) { 320 AdtPrefs prefs = AdtPrefs.getPrefs(); 321 mCheckFileCheckbox.setSelection(prefs.isLintOnSave()); 322 mCheckExportCheckbox.setSelection(prefs.isLintOnExport()); 323 } 324 325 mSeverities.clear(); 326 List<Issue> issues = mRegistry.getIssues(); 327 for (Issue issue : issues) { 328 Severity severity = mConfiguration.getSeverity(issue); 329 mSeverities.put(issue, severity); 330 } 331 332 if (refresh) { 333 mTreeViewer.refresh(); 334 } 335 } 336 337 private void storeSettings() { 338 // Lint on Save, Lint on Export 339 if (mCheckExportCheckbox != null) { 340 AdtPrefs prefs = AdtPrefs.getPrefs(); 341 prefs.setLintOnExport(mCheckExportCheckbox.getSelection()); 342 prefs.setLintOnSave(mCheckFileCheckbox.getSelection()); 343 } 344 345 if (mConfiguration == null) { 346 return; 347 } 348 349 mConfiguration.startBulkEditing(); 350 try { 351 // Severities 352 for (Map.Entry<Issue, Severity> entry : mSeverities.entrySet()) { 353 Issue issue = entry.getKey(); 354 Severity severity = entry.getValue(); 355 if (mConfiguration.getSeverity(issue) != severity) { 356 if ((severity == issue.getDefaultSeverity()) && issue.isEnabledByDefault()) { 357 severity = null; 358 } 359 mConfiguration.setSeverity(issue, severity); 360 } 361 } 362 } finally { 363 mConfiguration.finishBulkEditing(); 364 } 365 366 if (!mInitialSeverities.equals(mSeverities)) { 367 // Ask user whether we should re-run the rules. 368 MessageDialog dialog = new MessageDialog( 369 null, "Lint Settings Have Changed", null, 370 "The list of enabled checks has changed. Would you like to run lint now " + 371 "to update the results?", 372 MessageDialog.QUESTION, 373 new String[] { 374 "Yes", "No" 375 }, 376 0); // yes is the default 377 int result = dialog.open(); 378 if (result == 0) { 379 // Run lint on all the open Android projects 380 IWorkspace workspace = ResourcesPlugin.getWorkspace(); 381 IProject[] projects = workspace.getRoot().getProjects(); 382 List<IProject> androidProjects = new ArrayList<IProject>(projects.length); 383 for (IProject project : projects) { 384 if (project.isOpen() && BaseProjectHelper.isAndroidProject(project)) { 385 androidProjects.add(project); 386 } 387 } 388 389 EclipseLintRunner.startLint(androidProjects, null, null, false /*fatalOnly*/, 390 true /*show*/); 391 } 392 } 393 } 394 395 private void updateFilter() { 396 cancelPendingSearch(); 397 if (!mSearch.isDisposed()) { 398 // Clear selection before refiltering since otherwise it might be showing 399 // items no longer available in the list. 400 mTree.setSelection(new TreeItem[0]); 401 mDetailsText.setText(""); 402 try { 403 mIgnoreEvent = true; 404 mSeverityCombo.setText(""); 405 mSeverityCombo.setEnabled(false); 406 } finally { 407 mIgnoreEvent = false; 408 } 409 410 mTreeViewer.getContentProvider().inputChanged(mTreeViewer, null, mRegistry); 411 mTreeViewer.refresh(); 412 mTreeViewer.expandAll(); 413 } 414 } 415 416 private void cancelPendingSearch() { 417 if (mPendingUpdate != null) { 418 Shell shell = getShell(); 419 if (!shell.isDisposed()) { 420 getShell().getDisplay().timerExec(-1, mPendingUpdate); 421 } 422 mPendingUpdate = null; 423 } 424 } 425 426 private Runnable mPendingUpdate; 427 428 private void scheduleSearch() { 429 if (mPendingUpdate == null) { 430 mPendingUpdate = new Runnable() { 431 @Override 432 public void run() { 433 mPendingUpdate = null; 434 updateFilter(); 435 } 436 }; 437 } 438 getShell().getDisplay().timerExec(250 /*ms*/, mPendingUpdate); 439 } 440 441 // ---- Implements SelectionListener ---- 442 443 @Override 444 public void widgetSelected(SelectionEvent e) { 445 if (mIgnoreEvent) { 446 return; 447 } 448 449 Object source = e.getSource(); 450 if (source == mTree) { 451 TreeItem item = (TreeItem) e.item; 452 Object data = item != null ? item.getData() : null; 453 if (data instanceof Issue) { 454 Issue issue = (Issue) data; 455 String summary = issue.getBriefDescription(TextFormat.TEXT); 456 String explanation = issue.getExplanation(TextFormat.TEXT); 457 458 StringBuilder sb = new StringBuilder(summary.length() + explanation.length() + 20); 459 sb.append(summary); 460 sb.append('\n').append('\n'); 461 sb.append(explanation); 462 mDetailsText.setText(sb.toString()); 463 try { 464 mIgnoreEvent = true; 465 Severity severity = getSeverity(issue); 466 mSeverityCombo.select(severity.ordinal() + 1); // Skip the default option 467 mSeverityCombo.setEnabled(true); 468 } finally { 469 mIgnoreEvent = false; 470 } 471 } else { 472 mDetailsText.setText(""); 473 try { 474 mIgnoreEvent = true; 475 mSeverityCombo.setText(""); 476 mSeverityCombo.setEnabled(false); 477 } finally { 478 mIgnoreEvent = false; 479 } 480 } 481 } else if (source == mWorkspaceLink) { 482 int result = PreferencesUtil.createPreferenceDialogOn(getShell(), ID, 483 new String[] { ID }, null).open(); 484 if (result == Window.OK) { 485 loadSettings(true); 486 } 487 } else if (source == mSeverityCombo) { 488 int index = mSeverityCombo.getSelectionIndex(); 489 Issue issue = (Issue) mTree.getSelection()[0].getData(); 490 Severity severity; 491 if (index == -1 || index == 0) { 492 // "(Default)" 493 severity = issue.getDefaultSeverity(); 494 } else { 495 // -1: Skip the "(Default)" 496 severity = Severity.values()[index - 1]; 497 } 498 mSeverities.put(issue, severity); 499 mTreeViewer.refresh(); 500 } else if (source == mIncludeAll) { 501 List<Issue> issues = mRegistry.getIssues(); 502 for (Issue issue : issues) { 503 // The default severity is never ignore; for disabled-by-default 504 // issues the {@link Issue#isEnabledByDefault()} method is false instead 505 mSeverities.put(issue, issue.getDefaultSeverity()); 506 } 507 mTreeViewer.refresh(); 508 } else if (source == mIgnoreAll) { 509 List<Issue> issues = mRegistry.getIssues(); 510 for (Issue issue : issues) { 511 mSeverities.put(issue, Severity.IGNORE); 512 } 513 mTreeViewer.refresh(); 514 } else if (source == mSearch) { 515 updateFilter(); 516 } 517 } 518 519 private Severity getSeverity(Issue issue) { 520 Severity severity = mSeverities.get(issue); 521 if (severity != null) { 522 return severity; 523 } 524 525 return mConfiguration.getSeverity(issue); 526 } 527 528 @Override 529 public void widgetDefaultSelected(SelectionEvent e) { 530 Object source = e.getSource(); 531 if (source == mTree) { 532 widgetSelected(e); 533 } else if (source == mSearch) { 534 if (e.detail == SWT.CANCEL) { 535 // Cancel the search 536 mSearch.setText(""); 537 } 538 updateFilter(); 539 } 540 } 541 542 // ---- Implements ModifyListener ---- 543 @Override 544 public void modifyText(ModifyEvent e) { 545 if (e.getSource() == mSearch) { 546 scheduleSearch(); 547 } 548 } 549 550 // ---- Implements ControlListener ---- 551 552 @Override 553 public void controlMoved(ControlEvent e) { 554 } 555 556 @Override 557 public void controlResized(ControlEvent e) { 558 Rectangle r = mTree.getClientArea(); 559 int availableWidth = r.width; 560 561 mIdColumn.setWidth(ID_COLUMN_WIDTH); 562 availableWidth -= ID_COLUMN_WIDTH; 563 564 // Name absorbs everything else 565 mNameColumn.setWidth(availableWidth); 566 } 567 568 private boolean filterMatches(@NonNull String filter, @NonNull Issue issue) { 569 return (filter.startsWith("~") //$NON-NLS-1$ 570 && mConfiguration.getSeverity(issue).getDescription() 571 .toLowerCase(Locale.US).startsWith(filter.substring(1))) 572 || issue.getCategory().getName().toLowerCase(Locale.US).startsWith(filter) 573 || issue.getCategory().getFullName().toLowerCase(Locale.US).startsWith(filter) 574 || issue.getId().toLowerCase(Locale.US).contains(filter) 575 || issue.getBriefDescription(RAW).toLowerCase(Locale.US).contains(filter); 576 } 577 578 private class ContentProvider extends TreeNodeContentProvider { 579 private Map<Category, List<Issue>> mCategoryToIssues; 580 private Object[] mElements; 581 582 @Override 583 public Object[] getElements(Object inputElement) { 584 return mElements; 585 } 586 587 @Override 588 public boolean hasChildren(Object element) { 589 return element instanceof Category; 590 } 591 592 @Override 593 public Object[] getChildren(Object parentElement) { 594 assert mCategoryToIssues != null; 595 List<Issue> list = mCategoryToIssues.get(parentElement); 596 if (list == null) { 597 return new Object[0]; 598 } else { 599 return list.toArray(); 600 } 601 } 602 603 @Override 604 public Object getParent(Object element) { 605 return null; 606 } 607 608 @Override 609 public void inputChanged(final Viewer viewer, final Object oldInput, 610 final Object newInput) { 611 mCategoryToIssues = null; 612 613 String filter = mSearch.isDisposed() ? "" : mSearch.getText().trim(); 614 if (filter.length() == 0) { 615 filter = null; 616 } else { 617 filter = filter.toLowerCase(Locale.US); 618 } 619 620 mCategoryToIssues = new HashMap<Category, List<Issue>>(); 621 List<Issue> issues = mRegistry.getIssues(); 622 for (Issue issue : issues) { 623 if (filter == null || filterMatches(filter, issue)) { 624 List<Issue> list = mCategoryToIssues.get(issue.getCategory()); 625 if (list == null) { 626 list = new ArrayList<Issue>(); 627 mCategoryToIssues.put(issue.getCategory(), list); 628 } 629 list.add(issue); 630 } 631 } 632 633 if (filter == null) { 634 // Not filtering: show all categories 635 mElements = mRegistry.getCategories().toArray(); 636 } else { 637 // Filtering: only include categories that contain matches 638 if (mCategoryToIssues == null) { 639 getChildren(null); 640 } 641 642 // Preserve the current category order, so instead of 643 // just creating a list of the mCategoryToIssues keyset, add them 644 // in the order they appear in in the registry 645 List<Category> categories = new ArrayList<Category>(mCategoryToIssues.size()); 646 for (Category category : mRegistry.getCategories()) { 647 if (mCategoryToIssues.containsKey(category)) { 648 categories.add(category); 649 } 650 } 651 mElements = categories.toArray(); 652 } 653 } 654 } 655 656 private class LabelProvider implements ITableLabelProvider, IColorProvider { 657 658 @Override 659 public void addListener(ILabelProviderListener listener) { 660 } 661 662 @Override 663 public void dispose() { 664 } 665 666 @Override 667 public boolean isLabelProperty(Object element, String property) { 668 return true; 669 } 670 671 @Override 672 public void removeListener(ILabelProviderListener listener) { 673 } 674 675 @Override 676 public Image getColumnImage(Object element, int columnIndex) { 677 if (element instanceof Category) { 678 return null; 679 } 680 681 if (columnIndex == 1) { 682 Issue issue = (Issue) element; 683 Severity severity = mSeverities.get(issue); 684 if (severity == null) { 685 return null; 686 } 687 688 ISharedImages sharedImages = PlatformUI.getWorkbench().getSharedImages(); 689 switch (severity) { 690 case FATAL: 691 case ERROR: 692 return sharedImages.getImage(ISharedImages.IMG_OBJS_ERROR_TSK); 693 case WARNING: 694 return sharedImages.getImage(ISharedImages.IMG_OBJS_WARN_TSK); 695 case INFORMATIONAL: 696 return sharedImages.getImage(ISharedImages.IMG_OBJS_INFO_TSK); 697 case IGNORE: 698 return sharedImages.getImage(ISharedImages.IMG_ELCL_REMOVE_DISABLED); 699 } 700 } 701 return null; 702 } 703 704 @Override 705 public String getColumnText(Object element, int columnIndex) { 706 if (element instanceof Category) { 707 if (columnIndex == 0) { 708 return ((Category) element).getFullName(); 709 } else { 710 return null; 711 } 712 } 713 714 Issue issue = (Issue) element; 715 switch (columnIndex) { 716 case 0: 717 return issue.getId(); 718 case 1: 719 return issue.getBriefDescription(TEXT); 720 } 721 722 return null; 723 } 724 725 // ---- IColorProvider ---- 726 727 @Override 728 public Color getForeground(Object element) { 729 if (element instanceof Category) { 730 return mTree.getDisplay().getSystemColor(SWT.COLOR_INFO_FOREGROUND); 731 } 732 733 if (element instanceof Issue) { 734 Issue issue = (Issue) element; 735 Severity severity = mSeverities.get(issue); 736 if (severity == Severity.IGNORE) { 737 return mTree.getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY); 738 } 739 } 740 741 return null; 742 } 743 744 @Override 745 public Color getBackground(Object element) { 746 if (element instanceof Category) { 747 return mTree.getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND); 748 } 749 return null; 750 } 751 } 752 } 753