1 /* 2 * Copyright (C) 2007 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 17 package com.android.ide.eclipse.adt.internal.preferences; 18 19 import com.android.ide.eclipse.adt.AdtPlugin; 20 import com.android.ide.eclipse.adt.internal.sdk.Sdk; 21 import com.android.ide.eclipse.adt.internal.sdk.Sdk.ITargetChangeListener; 22 import com.android.sdklib.IAndroidTarget; 23 import com.android.sdkuilib.internal.widgets.SdkTargetSelector; 24 25 import org.eclipse.core.resources.IProject; 26 import org.eclipse.jface.preference.DirectoryFieldEditor; 27 import org.eclipse.jface.preference.FieldEditorPreferencePage; 28 import org.eclipse.jface.resource.JFaceResources; 29 import org.eclipse.swt.SWT; 30 import org.eclipse.swt.layout.GridData; 31 import org.eclipse.swt.widgets.Composite; 32 import org.eclipse.swt.widgets.Label; 33 import org.eclipse.swt.widgets.Text; 34 import org.eclipse.ui.IWorkbench; 35 import org.eclipse.ui.IWorkbenchPreferencePage; 36 37 import java.io.File; 38 39 /** 40 * This class represents a preference page that is contributed to the 41 * Preferences dialog. By subclassing <samp>FieldEditorPreferencePage</samp>, 42 * we can use the field support built into JFace that allows us to create a page 43 * that is small and knows how to save, restore and apply itself. 44 * <p> 45 * This page is used to modify preferences only. They are stored in the 46 * preference store that belongs to the main plug-in class. That way, 47 * preferences can be accessed directly via the preference store. 48 */ 49 50 public class AndroidPreferencePage extends FieldEditorPreferencePage implements 51 IWorkbenchPreferencePage { 52 53 private SdkDirectoryFieldEditor mDirectoryField; 54 55 public AndroidPreferencePage() { 56 super(GRID); 57 setPreferenceStore(AdtPlugin.getDefault().getPreferenceStore()); 58 setDescription(Messages.AndroidPreferencePage_Title); 59 } 60 61 /** 62 * Creates the field editors. Field editors are abstractions of the common 63 * GUI blocks needed to manipulate various types of preferences. Each field 64 * editor knows how to save and restore itself. 65 */ 66 @Override 67 public void createFieldEditors() { 68 69 mDirectoryField = new SdkDirectoryFieldEditor(AdtPrefs.PREFS_SDK_DIR, 70 Messages.AndroidPreferencePage_SDK_Location_, getFieldEditorParent()); 71 72 addField(mDirectoryField); 73 } 74 75 /* 76 * (non-Javadoc) 77 * 78 * @see org.eclipse.ui.IWorkbenchPreferencePage#init(org.eclipse.ui.IWorkbench) 79 */ 80 public void init(IWorkbench workbench) { 81 } 82 83 @Override 84 public void dispose() { 85 super.dispose(); 86 87 if (mDirectoryField != null) { 88 mDirectoryField.dispose(); 89 mDirectoryField = null; 90 } 91 } 92 93 /** 94 * Custom version of DirectoryFieldEditor which validates that the directory really 95 * contains an SDK. 96 * 97 * There's a known issue here, which is really a rare edge-case: if the pref dialog is open 98 * which a given sdk directory and the *content* of the directory changes such that the sdk 99 * state changed (i.e. from valid to invalid or vice versa), the pref panel will display or 100 * hide the error as appropriate but the pref panel will fail to validate the apply/ok buttons 101 * appropriately. The easy workaround is to cancel the pref panel and enter it again. 102 */ 103 private static class SdkDirectoryFieldEditor extends DirectoryFieldEditor { 104 105 private SdkTargetSelector mTargetSelector; 106 private TargetChangedListener mTargetChangeListener; 107 108 public SdkDirectoryFieldEditor(String name, String labelText, Composite parent) { 109 super(name, labelText, parent); 110 setEmptyStringAllowed(false); 111 } 112 113 /** 114 * Method declared on StringFieldEditor and overridden in DirectoryFieldEditor. 115 * Checks whether the text input field contains a valid directory. 116 * 117 * @return True if the apply/ok button should be enabled in the pref panel 118 */ 119 @Override 120 protected boolean doCheckState() { 121 String fileName = getTextControl().getText(); 122 fileName = fileName.trim(); 123 124 if (fileName.indexOf(',') >= 0 || fileName.indexOf(';') >= 0) { 125 setErrorMessage(Messages.AndroidPreferencePage_ERROR_Reserved_Char); 126 return false; // Apply/OK must be disabled 127 } 128 129 File file = new File(fileName); 130 if (!file.isDirectory()) { 131 setErrorMessage(JFaceResources.getString( 132 "DirectoryFieldEditor.errorMessage")); //$NON-NLS-1$ 133 return false; 134 } 135 136 boolean ok = AdtPlugin.getDefault().checkSdkLocationAndId(fileName, 137 new AdtPlugin.CheckSdkErrorHandler() { 138 @Override 139 public boolean handleError(String message) { 140 setErrorMessage(message.replaceAll("\n", " ")); //$NON-NLS-1$ //$NON-NLS-2$ 141 return false; // Apply/OK must be disabled 142 } 143 144 @Override 145 public boolean handleWarning(String message) { 146 showMessage(message.replaceAll("\n", " ")); //$NON-NLS-1$ //$NON-NLS-2$ 147 return true; // Apply/OK must be enabled 148 } 149 }); 150 if (ok) clearMessage(); 151 return ok; 152 } 153 154 @Override 155 public Text getTextControl(Composite parent) { 156 setValidateStrategy(VALIDATE_ON_KEY_STROKE); 157 return super.getTextControl(parent); 158 } 159 160 /* (non-Javadoc) 161 * Method declared on StringFieldEditor (and FieldEditor). 162 */ 163 @Override 164 protected void doFillIntoGrid(Composite parent, int numColumns) { 165 super.doFillIntoGrid(parent, numColumns); 166 167 GridData gd; 168 Label l = new Label(parent, SWT.NONE); 169 l.setText("Note: The list of SDK Targets below is only reloaded once you hit 'Apply' or 'OK'."); 170 gd = new GridData(GridData.FILL_HORIZONTAL); 171 gd.horizontalSpan = numColumns; 172 l.setLayoutData(gd); 173 174 try { 175 // We may not have an sdk if the sdk path pref is empty or not valid. 176 Sdk sdk = Sdk.getCurrent(); 177 IAndroidTarget[] targets = sdk != null ? sdk.getTargets() : null; 178 179 mTargetSelector = new SdkTargetSelector(parent, 180 targets, 181 false /*allowSelection*/); 182 gd = (GridData) mTargetSelector.getLayoutData(); 183 gd.horizontalSpan = numColumns; 184 185 if (mTargetChangeListener == null) { 186 mTargetChangeListener = new TargetChangedListener(); 187 AdtPlugin.getDefault().addTargetListener(mTargetChangeListener); 188 } 189 } catch (Exception e) { 190 // We need to catch *any* exception that arises here, otherwise it disables 191 // the whole pref panel. We can live without the Sdk target selector but 192 // not being able to actually set an sdk path. 193 AdtPlugin.log(e, "SdkTargetSelector failed"); 194 } 195 } 196 197 @Override 198 public void dispose() { 199 super.dispose(); 200 if (mTargetChangeListener != null) { 201 AdtPlugin.getDefault().removeTargetListener(mTargetChangeListener); 202 mTargetChangeListener = null; 203 } 204 } 205 206 private class TargetChangedListener implements ITargetChangeListener { 207 public void onSdkLoaded() { 208 if (mTargetSelector != null) { 209 // We may not have an sdk if the sdk path pref is empty or not valid. 210 Sdk sdk = Sdk.getCurrent(); 211 IAndroidTarget[] targets = sdk != null ? sdk.getTargets() : null; 212 213 mTargetSelector.setTargets(targets); 214 } 215 } 216 217 public void onProjectTargetChange(IProject changedProject) { 218 // do nothing. 219 } 220 221 public void onTargetLoaded(IAndroidTarget target) { 222 // do nothing. 223 } 224 } 225 } 226 } 227