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.lint;
17 
18 import static com.android.SdkConstants.DOT_JAVA;
19 import static com.android.SdkConstants.DOT_XML;
20 
21 import com.android.ide.eclipse.adt.AdtPlugin;
22 import com.android.ide.eclipse.adt.internal.editors.IconFactory;
23 import com.android.ide.eclipse.adt.internal.preferences.LintPreferencePage;
24 import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
25 import com.android.tools.lint.detector.api.LintUtils;
26 
27 import org.eclipse.core.resources.IFile;
28 import org.eclipse.core.resources.IFolder;
29 import org.eclipse.core.resources.IMarker;
30 import org.eclipse.core.resources.IProject;
31 import org.eclipse.core.resources.IResource;
32 import org.eclipse.core.runtime.CoreException;
33 import org.eclipse.core.runtime.NullProgressMonitor;
34 import org.eclipse.core.runtime.jobs.IJobChangeEvent;
35 import org.eclipse.core.runtime.jobs.IJobChangeListener;
36 import org.eclipse.core.runtime.jobs.Job;
37 import org.eclipse.jdt.core.IJavaProject;
38 import org.eclipse.jface.action.Action;
39 import org.eclipse.jface.action.IStatusLineManager;
40 import org.eclipse.jface.action.IToolBarManager;
41 import org.eclipse.jface.action.Separator;
42 import org.eclipse.jface.preference.IPreferenceNode;
43 import org.eclipse.jface.preference.PreferenceDialog;
44 import org.eclipse.jface.preference.PreferenceManager;
45 import org.eclipse.jface.preference.PreferenceNode;
46 import org.eclipse.jface.resource.ImageDescriptor;
47 import org.eclipse.jface.text.IDocument;
48 import org.eclipse.jface.text.IRegion;
49 import org.eclipse.jface.text.Region;
50 import org.eclipse.swt.SWT;
51 import org.eclipse.swt.custom.SashForm;
52 import org.eclipse.swt.events.SelectionEvent;
53 import org.eclipse.swt.events.SelectionListener;
54 import org.eclipse.swt.layout.GridData;
55 import org.eclipse.swt.layout.GridLayout;
56 import org.eclipse.swt.widgets.Composite;
57 import org.eclipse.swt.widgets.Display;
58 import org.eclipse.swt.widgets.Label;
59 import org.eclipse.swt.widgets.Shell;
60 import org.eclipse.swt.widgets.Text;
61 import org.eclipse.ui.IMemento;
62 import org.eclipse.ui.ISharedImages;
63 import org.eclipse.ui.IViewPart;
64 import org.eclipse.ui.IViewSite;
65 import org.eclipse.ui.IWorkbench;
66 import org.eclipse.ui.IWorkbenchPage;
67 import org.eclipse.ui.IWorkbenchWindow;
68 import org.eclipse.ui.PartInitException;
69 import org.eclipse.ui.PlatformUI;
70 import org.eclipse.ui.editors.text.TextFileDocumentProvider;
71 import org.eclipse.ui.part.ViewPart;
72 import org.eclipse.ui.texteditor.IDocumentProvider;
73 
74 import java.util.ArrayList;
75 import java.util.Collections;
76 import java.util.HashSet;
77 import java.util.List;
78 import java.util.Set;
79 
80 /**
81  * Eclipse View which shows lint warnings for the current project
82  */
83 public class LintViewPart extends ViewPart implements SelectionListener, IJobChangeListener {
84     /** The view id for this view part */
85     public static final String ID = "com.android.ide.eclipse.adt.internal.lint.LintViewPart"; //$NON-NLS-1$
86     private static final String QUICKFIX_DISABLED_ICON = "quickfix-disabled";         //$NON-NLS-1$
87     private static final String QUICKFIX_ICON = "quickfix";                           //$NON-NLS-1$
88     private static final String REFRESH_ICON = "refresh";                             //$NON-NLS-1$
89     private static final String EXPAND_DISABLED_ICON = "expandall-disabled";          //$NON-NLS-1$
90     private static final String EXPAND_ICON = "expandall";                            //$NON-NLS-1$
91     private static final String COLUMNS_ICON = "columns";                             //$NON-NLS-1$
92     private static final String OPTIONS_ICON = "options";                             //$NON-NLS-1$
93     private static final String IGNORE_THIS_ICON = "ignore-this";                     //$NON-NLS-1$
94     private static final String IGNORE_THIS_DISABLED_ICON = "ignore-this-disabled";   //$NON-NLS-1$
95     private static final String IGNORE_FILE_ICON = "ignore-file";                     //$NON-NLS-1$
96     private static final String IGNORE_FILE_DISABLED_ICON = "ignore-file-disabled";   //$NON-NLS-1$
97     private static final String IGNORE_PRJ_ICON = "ignore-project";                   //$NON-NLS-1$
98     private static final String IGNORE_PRJ_DISABLED_ICON = "ignore-project-disabled"; //$NON-NLS-1$
99     private static final String IGNORE_ALL_ICON = "ignore-all";                   //$NON-NLS-1$
100     private static final String IGNORE_ALL_DISABLED_ICON = "ignore-all-disabled"; //$NON-NLS-1$
101     private IMemento mMemento;
102     private LintList mLintView;
103     private Text mDetailsText;
104     private Label mErrorLabel;
105     private SashForm mSashForm;
106     private Action mFixAction;
107     private Action mRemoveAction;
108     private Action mIgnoreAction;
109     private Action mAlwaysIgnoreAction;
110     private Action mIgnoreFileAction;
111     private Action mIgnoreProjectAction;
112     private Action mRemoveAllAction;
113     private Action mRefreshAction;
114     private Action mExpandAll;
115     private Action mCollapseAll;
116     private Action mConfigureColumns;
117     private Action mOptions;
118 
119     /**
120      * Initial projects to show: this field is only briefly not null during the
121      * construction initiated by {@link #show(List)}
122      */
123     private static List<? extends IResource> sInitialResources;
124 
125     /**
126      * Constructs a new {@link LintViewPart}
127      */
LintViewPart()128     public LintViewPart() {
129     }
130 
131     @Override
init(IViewSite site, IMemento memento)132     public void init(IViewSite site, IMemento memento) throws PartInitException {
133         super.init(site, memento);
134         mMemento = memento;
135     }
136 
137     @Override
saveState(IMemento memento)138     public void saveState(IMemento memento) {
139         super.saveState(memento);
140 
141         mLintView.saveState(memento);
142     }
143 
144     @Override
dispose()145     public void dispose() {
146         if (mLintView != null) {
147             mLintView.dispose();
148             mLintView = null;
149         }
150         super.dispose();
151     }
152 
153     @Override
createPartControl(Composite parent)154     public void createPartControl(Composite parent) {
155         GridLayout gridLayout = new GridLayout(1, false);
156         gridLayout.verticalSpacing = 0;
157         gridLayout.marginWidth = 0;
158         gridLayout.marginHeight = 0;
159         parent.setLayout(gridLayout);
160 
161         mErrorLabel = new Label(parent, SWT.NONE);
162         mErrorLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1));
163 
164         mSashForm = new SashForm(parent, SWT.NONE);
165         mSashForm.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
166         mLintView = new LintList(getSite(), mSashForm, mMemento, false /*singleFile*/);
167 
168         mDetailsText = new Text(mSashForm,
169                 SWT.BORDER | SWT.READ_ONLY | SWT.WRAP | SWT.V_SCROLL | SWT.MULTI);
170         Display display = parent.getDisplay();
171         mDetailsText.setBackground(display.getSystemColor(SWT.COLOR_INFO_BACKGROUND));
172         mDetailsText.setForeground(display.getSystemColor(SWT.COLOR_INFO_FOREGROUND));
173 
174         mLintView.addSelectionListener(this);
175         mSashForm.setWeights(new int[] {8, 2});
176 
177         createActions();
178         initializeToolBar();
179 
180         // If there are currently running jobs, listen for them such that we can update the
181         // button state
182         refreshStopIcon();
183 
184         if (sInitialResources != null) {
185             mLintView.setResources(sInitialResources);
186             sInitialResources = null;
187         } else {
188             // No supplied context: show lint warnings for all projects
189             IJavaProject[] androidProjects = BaseProjectHelper.getAndroidProjects(null);
190             if (androidProjects.length > 0) {
191                 List<IResource> projects = new ArrayList<IResource>();
192                 for (IJavaProject project : androidProjects) {
193                     projects.add(project.getProject());
194                 }
195                 mLintView.setResources(projects);
196             }
197         }
198 
199         updateIssueCount();
200     }
201 
202     /**
203      * Create the actions.
204      */
createActions()205     private void createActions() {
206         ISharedImages sharedImages = PlatformUI.getWorkbench().getSharedImages();
207         IconFactory iconFactory = IconFactory.getInstance();
208         mFixAction = new LintViewAction("Fix", ACTION_FIX,
209                 iconFactory.getImageDescriptor(QUICKFIX_ICON),
210                 iconFactory.getImageDescriptor(QUICKFIX_DISABLED_ICON));
211 
212         mIgnoreAction = new LintViewAction("Suppress this error with an annotation/attribute",
213                 ACTION_IGNORE_THIS,
214                 iconFactory.getImageDescriptor(IGNORE_THIS_ICON),
215                 iconFactory.getImageDescriptor(IGNORE_THIS_DISABLED_ICON));
216         mIgnoreFileAction = new LintViewAction("Ignore in this file", ACTION_IGNORE_FILE,
217                 iconFactory.getImageDescriptor(IGNORE_FILE_ICON),
218                 iconFactory.getImageDescriptor(IGNORE_FILE_DISABLED_ICON));
219         mIgnoreProjectAction = new LintViewAction("Ignore in this project", ACTION_IGNORE_TYPE,
220                 iconFactory.getImageDescriptor(IGNORE_PRJ_ICON),
221                 iconFactory.getImageDescriptor(IGNORE_PRJ_DISABLED_ICON));
222         mAlwaysIgnoreAction = new LintViewAction("Always Ignore", ACTION_IGNORE_ALL,
223                 iconFactory.getImageDescriptor(IGNORE_ALL_ICON),
224                 iconFactory.getImageDescriptor(IGNORE_ALL_DISABLED_ICON));
225 
226         mRemoveAction = new LintViewAction("Remove", ACTION_REMOVE,
227                 sharedImages.getImageDescriptor(ISharedImages.IMG_ELCL_REMOVE),
228                 sharedImages.getImageDescriptor(ISharedImages.IMG_ELCL_REMOVE_DISABLED));
229         mRemoveAllAction = new LintViewAction("Remove All", ACTION_REMOVE_ALL,
230                 sharedImages.getImageDescriptor(ISharedImages.IMG_ELCL_REMOVEALL),
231                 sharedImages.getImageDescriptor(ISharedImages.IMG_ELCL_REMOVEALL_DISABLED));
232         mRefreshAction = new LintViewAction("Refresh (& Save Files)", ACTION_REFRESH,
233                 iconFactory.getImageDescriptor(REFRESH_ICON), null);
234         mRemoveAllAction.setEnabled(true);
235         mCollapseAll = new LintViewAction("Collapse All", ACTION_COLLAPSE,
236                 sharedImages.getImageDescriptor(ISharedImages.IMG_ELCL_COLLAPSEALL),
237                 sharedImages.getImageDescriptor(ISharedImages.IMG_ELCL_COLLAPSEALL_DISABLED));
238         mCollapseAll.setEnabled(true);
239         mExpandAll = new LintViewAction("Expand All", ACTION_EXPAND,
240                 iconFactory.getImageDescriptor(EXPAND_ICON),
241                 iconFactory.getImageDescriptor(EXPAND_DISABLED_ICON));
242         mExpandAll.setEnabled(true);
243 
244         mConfigureColumns = new LintViewAction("Configure Columns...", ACTION_COLUMNS,
245                 iconFactory.getImageDescriptor(COLUMNS_ICON),
246                 null);
247 
248         mOptions = new LintViewAction("Options...", ACTION_OPTIONS,
249                 iconFactory.getImageDescriptor(OPTIONS_ICON),
250                 null);
251 
252         enableActions(Collections.<IMarker>emptyList(), false /*updateWidgets*/);
253     }
254 
255     /**
256      * Initialize the toolbar.
257      */
initializeToolBar()258     private void initializeToolBar() {
259         IToolBarManager toolbarManager = getViewSite().getActionBars().getToolBarManager();
260         toolbarManager.add(mRefreshAction);
261         toolbarManager.add(mFixAction);
262         toolbarManager.add(mIgnoreAction);
263         toolbarManager.add(mIgnoreFileAction);
264         toolbarManager.add(mIgnoreProjectAction);
265         toolbarManager.add(mAlwaysIgnoreAction);
266         toolbarManager.add(new Separator());
267         toolbarManager.add(mRemoveAction);
268         toolbarManager.add(mRemoveAllAction);
269         toolbarManager.add(new Separator());
270         toolbarManager.add(mExpandAll);
271         toolbarManager.add(mCollapseAll);
272         toolbarManager.add(mConfigureColumns);
273         toolbarManager.add(mOptions);
274     }
275 
276     @Override
setFocus()277     public void setFocus() {
278         mLintView.setFocus();
279     }
280 
281     /**
282      * Sets the resource associated with the lint view
283      *
284      * @param resources the associated resources
285      */
setResources(List<? extends IResource> resources)286     public void setResources(List<? extends IResource> resources) {
287         mLintView.setResources(resources);
288 
289         // Refresh the stop/refresh icon status
290         refreshStopIcon();
291     }
292 
refreshStopIcon()293     private void refreshStopIcon() {
294         Job[] currentJobs = LintJob.getCurrentJobs();
295         if (currentJobs.length > 0) {
296             ISharedImages sharedImages = PlatformUI.getWorkbench().getSharedImages();
297             mRefreshAction.setImageDescriptor(sharedImages.getImageDescriptor(
298                     ISharedImages.IMG_ELCL_STOP));
299             for (Job job : currentJobs) {
300                 job.addJobChangeListener(this);
301             }
302         } else {
303             mRefreshAction.setImageDescriptor(
304                     IconFactory.getInstance().getImageDescriptor(REFRESH_ICON));
305 
306         }
307     }
308 
309     // ---- Implements SelectionListener ----
310 
311     @Override
widgetSelected(SelectionEvent e)312     public void widgetSelected(SelectionEvent e) {
313         List<IMarker> markers = mLintView.getSelectedMarkers();
314         if (markers.size() != 1) {
315             mDetailsText.setText(""); //$NON-NLS-1$
316         } else {
317             mDetailsText.setText(EclipseLintClient.describe(markers.get(0)));
318         }
319 
320         IStatusLineManager status = getViewSite().getActionBars().getStatusLineManager();
321         status.setMessage(mDetailsText.getText());
322 
323         updateIssueCount();
324 
325         enableActions(markers, true /* updateWidgets */);
326     }
327 
enableActions(List<IMarker> markers, boolean updateWidgets)328     private void enableActions(List<IMarker> markers, boolean updateWidgets) {
329         // Update enabled state of actions
330         boolean hasSelection = markers.size() > 0;
331         boolean canFix = hasSelection;
332         for (IMarker marker : markers) {
333             if (!LintFix.hasFix(EclipseLintClient.getId(marker))) {
334                 canFix = false;
335                 break;
336             }
337 
338             // Some fixes cannot be run in bulk
339             if (markers.size() > 1) {
340                 List<LintFix> fixes = LintFix.getFixes(EclipseLintClient.getId(marker), marker);
341                 if (fixes == null || !fixes.get(0).isBulkCapable()) {
342                     canFix = false;
343                     break;
344                 }
345             }
346         }
347 
348         boolean haveFile = false;
349         boolean isJavaOrXml = true;
350         for (IMarker marker : markers) {
351             IResource resource = marker.getResource();
352             if (resource instanceof IFile || resource instanceof IFolder) {
353                 haveFile = true;
354                 String name = resource.getName();
355                 if (!LintUtils.endsWith(name, DOT_XML) && !LintUtils.endsWith(name, DOT_JAVA)) {
356                     isJavaOrXml = false;
357                 }
358                 break;
359             }
360         }
361 
362         mFixAction.setEnabled(canFix);
363         mIgnoreAction.setEnabled(hasSelection && haveFile && isJavaOrXml);
364         mIgnoreFileAction.setEnabled(hasSelection && haveFile);
365         mIgnoreProjectAction.setEnabled(hasSelection);
366         mAlwaysIgnoreAction.setEnabled(hasSelection);
367         mRemoveAction.setEnabled(hasSelection);
368 
369         if (updateWidgets) {
370             getViewSite().getActionBars().getToolBarManager().update(false);
371         }
372     }
373 
374     @Override
widgetDefaultSelected(SelectionEvent e)375     public void widgetDefaultSelected(SelectionEvent e) {
376         Object source = e.getSource();
377         if (source == mLintView.getTreeViewer().getControl()) {
378             // Jump to editor
379             List<IMarker> selection = mLintView.getSelectedMarkers();
380             if (selection.size() > 0) {
381                 EclipseLintClient.showMarker(selection.get(0));
382             }
383         }
384     }
385 
386     // --- Implements IJobChangeListener ----
387 
388     @Override
done(IJobChangeEvent event)389     public void done(IJobChangeEvent event) {
390         mRefreshAction.setImageDescriptor(
391                 IconFactory.getInstance().getImageDescriptor(REFRESH_ICON));
392 
393         if (!mLintView.isDisposed()) {
394             mLintView.getDisplay().asyncExec(new Runnable()  {
395                 @Override
396                 public void run() {
397                     if (!mLintView.isDisposed()) {
398                         updateIssueCount();
399                     }
400                 }
401             });
402         }
403     }
404 
updateIssueCount()405     private void updateIssueCount() {
406         int errors = mLintView.getErrorCount();
407         int warnings = mLintView.getWarningCount();
408         mErrorLabel.setText(String.format("%1$d errors, %2$d warnings", errors, warnings));
409     }
410 
411     @Override
aboutToRun(IJobChangeEvent event)412     public void aboutToRun(IJobChangeEvent event) {
413     }
414 
415     @Override
awake(IJobChangeEvent event)416     public void awake(IJobChangeEvent event) {
417     }
418 
419     @Override
running(IJobChangeEvent event)420     public void running(IJobChangeEvent event) {
421     }
422 
423     @Override
scheduled(IJobChangeEvent event)424     public void scheduled(IJobChangeEvent event) {
425     }
426 
427     @Override
sleeping(IJobChangeEvent event)428     public void sleeping(IJobChangeEvent event) {
429     }
430 
431     // ---- Actions ----
432 
433     private static final int ACTION_REFRESH = 1;
434     private static final int ACTION_FIX = 2;
435     private static final int ACTION_IGNORE_THIS = 3;
436     private static final int ACTION_IGNORE_FILE = 4;
437     private static final int ACTION_IGNORE_TYPE = 5;
438     private static final int ACTION_IGNORE_ALL = 6;
439     private static final int ACTION_REMOVE = 7;
440     private static final int ACTION_REMOVE_ALL = 8;
441     private static final int ACTION_COLLAPSE = 9;
442     private static final int ACTION_EXPAND = 10;
443     private static final int ACTION_COLUMNS = 11;
444     private static final int ACTION_OPTIONS = 12;
445 
446     private class LintViewAction extends Action {
447 
448         private final int mAction;
449 
LintViewAction(String label, int action, ImageDescriptor imageDesc, ImageDescriptor disabledImageDesc)450         private LintViewAction(String label, int action,
451                 ImageDescriptor imageDesc, ImageDescriptor disabledImageDesc) {
452             super(label);
453             mAction = action;
454             setImageDescriptor(imageDesc);
455             if (disabledImageDesc != null) {
456                 setDisabledImageDescriptor(disabledImageDesc);
457             }
458         }
459 
460         @Override
run()461         public void run() {
462             switch (mAction) {
463                 case ACTION_REFRESH: {
464                     IWorkbench workbench = PlatformUI.getWorkbench();
465                     if (workbench != null) {
466                         workbench.saveAllEditors(false /*confirm*/);
467                     }
468 
469                     Job[] jobs = LintJob.getCurrentJobs();
470                     if (jobs.length > 0) {
471                         EclipseLintRunner.cancelCurrentJobs(false);
472                     } else {
473                         List<? extends IResource> resources = mLintView.getResources();
474                         if (resources == null) {
475                             return;
476                         }
477                         Job job = EclipseLintRunner.startLint(resources, null, null,
478                                 false /*fatalOnly*/, false /*show*/);
479                         if (job != null && workbench != null) {
480                             job.addJobChangeListener(LintViewPart.this);
481                             ISharedImages sharedImages = workbench.getSharedImages();
482                             setImageDescriptor(sharedImages.getImageDescriptor(
483                                     ISharedImages.IMG_ELCL_STOP));
484                         }
485                     }
486                     break;
487                 }
488                 case ACTION_FIX: {
489                     List<IMarker> markers = mLintView.getSelectedMarkers();
490                     for (IMarker marker : markers) {
491                         List<LintFix> fixes = LintFix.getFixes(EclipseLintClient.getId(marker),
492                                 marker);
493                         if (fixes == null) {
494                             continue;
495                         }
496                         LintFix fix = fixes.get(0);
497                         IResource resource = marker.getResource();
498                         if (fix.needsFocus() && resource instanceof IFile) {
499                             IRegion region = null;
500                             try {
501                                 int start = marker.getAttribute(IMarker.CHAR_START, -1);
502                                 int end = marker.getAttribute(IMarker.CHAR_END, -1);
503                                 if (start != -1) {
504                                     region = new Region(start, end - start);
505                                 }
506                                 AdtPlugin.openFile((IFile) resource, region);
507                             } catch (PartInitException e) {
508                                 AdtPlugin.log(e, "Can't open file %1$s", resource);
509                             }
510                         }
511                         IDocumentProvider provider = new TextFileDocumentProvider();
512                         try {
513                             provider.connect(resource);
514                             IDocument document = provider.getDocument(resource);
515                             if (document != null) {
516                                 fix.apply(document);
517                                 if (!fix.needsFocus()) {
518                                     provider.saveDocument(new NullProgressMonitor(), resource,
519                                             document,  true /*overwrite*/);
520                                 }
521                             }
522                         } catch (Exception e) {
523                             AdtPlugin.log(e, "Did not find associated editor to apply fix: %1$s",
524                                     resource.getName());
525                         } finally {
526                             provider.disconnect(resource);
527                         }
528                     }
529                     break;
530                 }
531                 case ACTION_REMOVE: {
532                     for (IMarker marker : mLintView.getSelectedMarkers()) {
533                         try {
534                             marker.delete();
535                         } catch (CoreException e) {
536                             AdtPlugin.log(e, null);
537                         }
538                     }
539                     break;
540                 }
541                 case ACTION_REMOVE_ALL: {
542                     List<? extends IResource> resources = mLintView.getResources();
543                     if (resources != null) {
544                         for (IResource resource : resources) {
545                             EclipseLintClient.clearMarkers(resource);
546                         }
547                     }
548                     break;
549                 }
550                 case ACTION_IGNORE_ALL:
551                     assert false;
552                     break;
553                 case ACTION_IGNORE_TYPE:
554                 case ACTION_IGNORE_FILE: {
555                     boolean ignoreInFile = mAction == ACTION_IGNORE_FILE;
556                     for (IMarker marker : mLintView.getSelectedMarkers()) {
557                         String id = EclipseLintClient.getId(marker);
558                         if (id != null) {
559                             IResource resource = marker.getResource();
560                             LintFixGenerator.suppressDetector(id, true,
561                                     ignoreInFile ? resource : resource.getProject(),
562                                     ignoreInFile);
563                         }
564                     }
565                     break;
566                 }
567                 case ACTION_IGNORE_THIS: {
568                     for (IMarker marker : mLintView.getSelectedMarkers()) {
569                         LintFixGenerator.addSuppressAnnotation(marker);
570                     }
571                     break;
572                 }
573                 case ACTION_COLLAPSE: {
574                     mLintView.collapseAll();
575                     break;
576                 }
577                 case ACTION_EXPAND: {
578                     mLintView.expandAll();
579                     break;
580                 }
581                 case ACTION_COLUMNS: {
582                     mLintView.configureColumns();
583                     break;
584                 }
585                 case ACTION_OPTIONS: {
586                     PreferenceManager manager = new PreferenceManager();
587 
588                     LintPreferencePage page = new LintPreferencePage();
589                     String title = "Default/Global Settings";
590                     page.setTitle(title);
591                     IPreferenceNode node = new PreferenceNode(title, page);
592                     manager.addToRoot(node);
593 
594 
595                     List<? extends IResource> resources = mLintView.getResources();
596                     if (resources != null) {
597                         Set<IProject> projects = new HashSet<IProject>();
598                         for (IResource resource : resources) {
599                             projects.add(resource.getProject());
600                         }
601                         if (projects.size() > 0) {
602                             for (IProject project : projects) {
603                                 page = new LintPreferencePage();
604                                 page.setTitle(String.format("Settings for %1$s",
605                                         project.getName()));
606                                 page.setElement(project);
607                                 node = new PreferenceNode(project.getName(), page);
608                                 manager.addToRoot(node);
609                             }
610                         }
611                     }
612 
613                     Shell shell = LintViewPart.this.getSite().getShell();
614                     PreferenceDialog dialog = new PreferenceDialog(shell, manager);
615                     dialog.create();
616                     dialog.setSelectedNode(title);
617                     dialog.open();
618                     break;
619                 }
620                 default:
621                     assert false : mAction;
622             }
623             updateIssueCount();
624         }
625     }
626 
627     /**
628      * Shows or reconfigures the LintView to show the lint warnings for the
629      * given project
630      *
631      * @param projects the projects to show lint warnings for
632      */
show(List<? extends IResource> projects)633     public static void show(List<? extends IResource> projects) {
634         IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
635         if (window != null) {
636             IWorkbenchPage page = window.getActivePage();
637             if (page != null) {
638                 try {
639                     // Pass initial project context via static field read by constructor
640                     sInitialResources = projects;
641                     IViewPart view = page.showView(LintViewPart.ID, null,
642                             IWorkbenchPage.VIEW_ACTIVATE);
643                     if (sInitialResources != null && view instanceof LintViewPart) {
644                         // The view must be showing already since the constructor was not
645                         // run, so reconfigure the view instead
646                         LintViewPart lintView = (LintViewPart) view;
647                         lintView.setResources(projects);
648                     }
649                 } catch (PartInitException e) {
650                     AdtPlugin.log(e, "Cannot open Lint View");
651                 } finally {
652                     sInitialResources = null;
653                 }
654             }
655         }
656     }
657 }
658