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 com.android.annotations.NonNull; 19 import com.android.annotations.Nullable; 20 import com.android.ide.eclipse.adt.AdtPlugin; 21 import com.android.ide.eclipse.adt.AdtUtils; 22 import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor; 23 import com.android.ide.eclipse.adt.internal.editors.IconFactory; 24 25 import org.eclipse.core.resources.IFile; 26 import org.eclipse.core.resources.IMarker; 27 import org.eclipse.core.resources.IResource; 28 import org.eclipse.core.runtime.IStatus; 29 import org.eclipse.jface.dialogs.IDialogConstants; 30 import org.eclipse.jface.dialogs.TitleAreaDialog; 31 import org.eclipse.swt.SWT; 32 import org.eclipse.swt.events.SelectionEvent; 33 import org.eclipse.swt.events.SelectionListener; 34 import org.eclipse.swt.graphics.Point; 35 import org.eclipse.swt.layout.GridData; 36 import org.eclipse.swt.layout.GridLayout; 37 import org.eclipse.swt.widgets.Button; 38 import org.eclipse.swt.widgets.Composite; 39 import org.eclipse.swt.widgets.Control; 40 import org.eclipse.swt.widgets.Display; 41 import org.eclipse.swt.widgets.Label; 42 import org.eclipse.swt.widgets.Shell; 43 import org.eclipse.swt.widgets.Text; 44 import org.eclipse.ui.IEditorPart; 45 import org.eclipse.ui.IWorkbenchPage; 46 import org.eclipse.ui.IWorkbenchPartSite; 47 import org.eclipse.ui.PlatformUI; 48 import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; 49 50 import java.util.Collections; 51 import java.util.HashSet; 52 import java.util.List; 53 import java.util.Set; 54 55 @SuppressWarnings("restriction") // WST DOM access 56 class LintListDialog extends TitleAreaDialog implements SelectionListener { 57 private static final String PROJECT_LOGO_LARGE = "android-64"; //$NON-NLS-1$ 58 private final IFile mFile; 59 private final IEditorPart mEditor; 60 private Button mFixButton; 61 private Button mIgnoreButton; 62 private Button mIgnoreAllButton; 63 private Button mShowButton; 64 private Text mDetailsText; 65 private Button mIgnoreTypeButton; 66 private LintList mList; 67 68 LintListDialog( 69 @NonNull Shell parentShell, 70 @NonNull IFile file, 71 @Nullable IEditorPart editor) { 72 super(parentShell); 73 mFile = file; 74 mEditor = editor; 75 setHelpAvailable(false); 76 } 77 78 @Override 79 protected void setShellStyle(int newShellStyle) { 80 // Allow resize 81 super.setShellStyle(newShellStyle | SWT.TITLE | SWT.MODELESS | SWT.RESIZE); 82 } 83 84 @Override 85 public boolean close() { 86 mList.dispose(); 87 return super.close(); 88 } 89 90 @Override 91 protected Control createContents(Composite parent) { 92 Control contents = super.createContents(parent); 93 setTitle("Lint Warnings in Layout"); 94 setMessage("Lint Errors found for the current layout:"); 95 setTitleImage(IconFactory.getInstance().getIcon(PROJECT_LOGO_LARGE)); 96 97 return contents; 98 } 99 100 @SuppressWarnings("unused") // SWT constructors have side effects, they are not unused 101 @Override 102 protected Control createDialogArea(Composite parent) { 103 Composite area = (Composite) super.createDialogArea(parent); 104 Composite container = new Composite(area, SWT.NONE); 105 container.setLayoutData(new GridData(GridData.FILL_BOTH)); 106 107 container.setLayout(new GridLayout(2, false)); 108 IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); 109 IWorkbenchPartSite site = null; 110 if (page.getActivePart() != null) { 111 site = page.getActivePart().getSite(); 112 } 113 114 mList = new LintList(site, container, null /*memento*/, true /*singleFile*/); 115 mList.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 6)); 116 117 mShowButton = new Button(container, SWT.NONE); 118 mShowButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1)); 119 mShowButton.setText("Show"); 120 mShowButton.setToolTipText("Opens the editor to reveal the XML with the issue"); 121 mShowButton.addSelectionListener(this); 122 123 mFixButton = new Button(container, SWT.NONE); 124 mFixButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1)); 125 mFixButton.setText("Fix"); 126 mFixButton.setToolTipText("Automatically corrects the problem, if possible"); 127 mFixButton.setEnabled(false); 128 mFixButton.addSelectionListener(this); 129 130 mIgnoreButton = new Button(container, SWT.NONE); 131 mIgnoreButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1)); 132 mIgnoreButton.setText("Suppress Issue"); 133 mIgnoreButton.setToolTipText("Adds a special attribute in the layout to suppress this specific warning"); 134 mIgnoreButton.addSelectionListener(this); 135 136 mIgnoreAllButton = new Button(container, SWT.NONE); 137 mIgnoreAllButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1)); 138 mIgnoreAllButton.setText("Suppress in Layout"); 139 mIgnoreAllButton.setEnabled(mEditor instanceof AndroidXmlEditor); 140 mIgnoreAllButton.setToolTipText("Adds an attribute on the root element to suppress all issues of this type in this layout"); 141 mIgnoreAllButton.addSelectionListener(this); 142 143 mIgnoreTypeButton = new Button(container, SWT.NONE); 144 mIgnoreTypeButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1)); 145 mIgnoreTypeButton.setText("Disable Issue Type"); 146 mIgnoreTypeButton.setToolTipText("Turns off checking for this type of error everywhere"); 147 mIgnoreTypeButton.addSelectionListener(this); 148 149 new Label(container, SWT.NONE); 150 151 mDetailsText = new Text(container, SWT.BORDER | SWT.READ_ONLY | SWT.WRAP 152 | SWT.V_SCROLL | SWT.MULTI); 153 Display display = parent.getDisplay(); 154 mDetailsText.setBackground(display.getSystemColor(SWT.COLOR_INFO_BACKGROUND)); 155 mDetailsText.setForeground(display.getSystemColor(SWT.COLOR_INFO_FOREGROUND)); 156 GridData gdText = new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1); 157 gdText.heightHint = 80; 158 mDetailsText.setLayoutData(gdText); 159 160 new Label(container, SWT.NONE); 161 162 mList.addSelectionListener(this); 163 164 mList.setResources(Collections.<IResource>singletonList(mFile)); 165 mList.selectFirst(); 166 if (mList.getSelectedMarkers().size() > 0) { 167 updateSelectionState(); 168 } 169 170 return area; 171 } 172 173 /** 174 * Create contents of the button bar. 175 */ 176 @Override 177 protected void createButtonsForButtonBar(Composite parent) { 178 createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL, true); 179 } 180 181 /** 182 * Return the initial size of the dialog. 183 */ 184 @Override 185 protected Point getInitialSize() { 186 return new Point(600, 400); 187 } 188 189 private void selectMarker(IMarker marker) { 190 if (marker == null) { 191 mDetailsText.setText(""); //$NON-NLS-1$ 192 return; 193 } 194 195 mDetailsText.setText(EclipseLintClient.describe(marker)); 196 } 197 198 // ---- Implements SelectionListener ---- 199 200 @Override 201 public void widgetSelected(SelectionEvent e) { 202 Object source = e.getSource(); 203 if (source == mList.getTreeViewer().getControl()) { 204 // Enable/disable buttons 205 updateSelectionState(); 206 } else if (source == mShowButton) { 207 List<IMarker> selection = mList.getSelectedMarkers(); 208 if (selection.size() > 0) { 209 EclipseLintClient.showMarker(selection.get(0)); 210 } 211 } else if (source == mFixButton) { 212 List<IMarker> selection = mList.getSelectedMarkers(); 213 for (IMarker marker : selection) { 214 List<LintFix> fixes = LintFix.getFixes(EclipseLintClient.getId(marker), marker); 215 if (fixes == null) { 216 continue; 217 } 218 LintFix fix = fixes.get(0); 219 IEditorPart editor = AdtUtils.getActiveEditor(); 220 if (editor instanceof AndroidXmlEditor) { 221 IStructuredDocument doc = ((AndroidXmlEditor) editor).getStructuredDocument(); 222 fix.apply(doc); 223 if (fix.needsFocus()) { 224 close(); 225 } 226 } else { 227 AdtPlugin.log(IStatus.ERROR, "Did not find associated editor to apply fix"); 228 } 229 } 230 } else if (source == mIgnoreTypeButton) { 231 for (IMarker marker : mList.getSelectedMarkers()) { 232 String id = EclipseLintClient.getId(marker); 233 if (id != null) { 234 LintFixGenerator.suppressDetector(id, true, mFile, true /*all*/); 235 } 236 } 237 } else if (source == mIgnoreButton) { 238 for (IMarker marker : mList.getSelectedMarkers()) { 239 LintFixGenerator.addSuppressAnnotation(marker); 240 } 241 } else if (source == mIgnoreAllButton) { 242 Set<String> ids = new HashSet<String>(); 243 for (IMarker marker : mList.getSelectedMarkers()) { 244 String id = EclipseLintClient.getId(marker); 245 if (id != null && !ids.contains(id)) { 246 ids.add(id); 247 if (mEditor instanceof AndroidXmlEditor) { 248 AndroidXmlEditor editor = (AndroidXmlEditor) mEditor; 249 AddSuppressAttribute fix = AddSuppressAttribute.createFixForAll(editor, 250 marker, id); 251 if (fix != null) { 252 IStructuredDocument document = editor.getStructuredDocument(); 253 fix.apply(document); 254 } 255 } 256 } 257 } 258 mList.refresh(); 259 } 260 } 261 262 private void updateSelectionState() { 263 List<IMarker> selection = mList.getSelectedMarkers(); 264 265 if (selection.size() == 1) { 266 selectMarker(selection.get(0)); 267 } else { 268 selectMarker(null); 269 } 270 271 boolean canFix = selection.size() > 0; 272 for (IMarker marker : selection) { 273 if (!LintFix.hasFix(EclipseLintClient.getId(marker))) { 274 canFix = false; 275 break; 276 } 277 278 // Some fixes cannot be run in bulk 279 if (selection.size() > 1) { 280 List<LintFix> fixes = LintFix.getFixes(EclipseLintClient.getId(marker), marker); 281 if (fixes == null || !fixes.get(0).isBulkCapable()) { 282 canFix = false; 283 break; 284 } 285 } 286 } 287 288 mFixButton.setEnabled(canFix); 289 } 290 291 @Override 292 public void widgetDefaultSelected(SelectionEvent e) { 293 Object source = e.getSource(); 294 if (source == mList.getTreeViewer().getControl()) { 295 // Jump to editor 296 List<IMarker> selection = mList.getSelectedMarkers(); 297 if (selection.size() > 0) { 298 EclipseLintClient.showMarker(selection.get(0)); 299 close(); 300 } 301 } 302 } 303 } 304