OSDN Git Service

Merge change 6506
[android-x86/development.git] / tools / sdkmanager / libs / sdkuilib / src / com / android / sdkuilib / internal / repository / RemotePackagesPage.java
1 /*\r
2  * Copyright (C) 2009 The Android Open Source Project\r
3  *\r
4  * Licensed under the Apache License, Version 2.0 (the "License");\r
5  * you may not use this file except in compliance with the License.\r
6  * You may obtain a copy of the License at\r
7  *\r
8  *      http://www.apache.org/licenses/LICENSE-2.0\r
9  *\r
10  * Unless required by applicable law or agreed to in writing, software\r
11  * distributed under the License is distributed on an "AS IS" BASIS,\r
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
13  * See the License for the specific language governing permissions and\r
14  * limitations under the License.\r
15  */\r
16 \r
17 package com.android.sdkuilib.internal.repository;\r
18 \r
19 \r
20 import com.android.sdklib.internal.repository.Archive;\r
21 import com.android.sdklib.internal.repository.IDescription;\r
22 import com.android.sdklib.internal.repository.Package;\r
23 import com.android.sdklib.internal.repository.RepoSource;\r
24 import com.android.sdkuilib.repository.UpdaterWindow.ISdkListener;\r
25 \r
26 import org.eclipse.jface.dialogs.IInputValidator;\r
27 import org.eclipse.jface.dialogs.InputDialog;\r
28 import org.eclipse.jface.dialogs.MessageDialog;\r
29 import org.eclipse.jface.viewers.CheckStateChangedEvent;\r
30 import org.eclipse.jface.viewers.CheckboxTreeViewer;\r
31 import org.eclipse.jface.viewers.ICheckStateListener;\r
32 import org.eclipse.jface.viewers.ISelection;\r
33 import org.eclipse.jface.viewers.ITreeContentProvider;\r
34 import org.eclipse.jface.viewers.ITreeSelection;\r
35 import org.eclipse.jface.window.Window;\r
36 import org.eclipse.swt.SWT;\r
37 import org.eclipse.swt.events.ControlAdapter;\r
38 import org.eclipse.swt.events.ControlEvent;\r
39 import org.eclipse.swt.events.SelectionAdapter;\r
40 import org.eclipse.swt.events.SelectionEvent;\r
41 import org.eclipse.swt.graphics.Rectangle;\r
42 import org.eclipse.swt.layout.GridData;\r
43 import org.eclipse.swt.layout.GridLayout;\r
44 import org.eclipse.swt.widgets.Button;\r
45 import org.eclipse.swt.widgets.Composite;\r
46 import org.eclipse.swt.widgets.Group;\r
47 import org.eclipse.swt.widgets.Label;\r
48 import org.eclipse.swt.widgets.Tree;\r
49 import org.eclipse.swt.widgets.TreeColumn;\r
50 \r
51 import java.util.ArrayList;\r
52 \r
53 \r
54 public class RemotePackagesPage extends Composite implements ISdkListener {\r
55 \r
56     private final UpdaterData mUpdaterData;\r
57 \r
58     private CheckboxTreeViewer mTreeViewerSources;\r
59     private Tree mTreeSources;\r
60     private TreeColumn mColumnSource;\r
61     private Button mUpdateOnlyCheckBox;\r
62     private Group mDescriptionContainer;\r
63     private Button mAddSiteButton;\r
64     private Button mDeleteSiteButton;\r
65     private Label mPlaceholder3;\r
66     private Button mRefreshButton;\r
67     private Button mInstallSelectedButton;\r
68     private Label mDescriptionLabel;\r
69 \r
70 \r
71 \r
72     /**\r
73      * Create the composite.\r
74      * @param parent The parent of the composite.\r
75      * @param updaterData An instance of {@link UpdaterData}.\r
76      */\r
77     RemotePackagesPage(Composite parent, UpdaterData updaterData) {\r
78         super(parent, SWT.BORDER);\r
79 \r
80         mUpdaterData = updaterData;\r
81 \r
82         createContents(this);\r
83         postCreate();  //$hide$\r
84     }\r
85 \r
86     private void createContents(Composite parent) {\r
87         parent.setLayout(new GridLayout(5, false));\r
88 \r
89         mTreeViewerSources = new CheckboxTreeViewer(parent, SWT.BORDER);\r
90         mTreeViewerSources.addCheckStateListener(new ICheckStateListener() {\r
91             public void checkStateChanged(CheckStateChangedEvent event) {\r
92                 onTreeCheckStateChanged(event); //$hide$\r
93             }\r
94         });\r
95         mTreeSources = mTreeViewerSources.getTree();\r
96         mTreeSources.addSelectionListener(new SelectionAdapter() {\r
97             @Override\r
98             public void widgetSelected(SelectionEvent e) {\r
99                 onTreeSelected(); //$hide$\r
100             }\r
101         });\r
102         mTreeSources.setHeaderVisible(true);\r
103         mTreeSources.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 5, 1));\r
104 \r
105         mColumnSource = new TreeColumn(mTreeSources, SWT.NONE);\r
106         mColumnSource.setWidth(289);\r
107         mColumnSource.setText("Sources, Packages and Archives");\r
108 \r
109         Composite composite = new Composite(parent, SWT.NONE);\r
110         composite.setLayoutData(\r
111                 new GridData(SWT.FILL, SWT.BEGINNING, false, false, 5, 1));\r
112         GridLayout gl;\r
113         composite.setLayout(gl = new GridLayout(2, false));\r
114         gl.marginHeight = gl.marginWidth = 0;\r
115         // add an empty composite\r
116         Composite spacer = new Composite(composite, SWT.NONE);\r
117         GridData gd;\r
118         spacer.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));\r
119         gd.heightHint = 0;\r
120 \r
121         mUpdateOnlyCheckBox = new Button(composite, SWT.CHECK);\r
122         mUpdateOnlyCheckBox.setText("Display updates only");\r
123         mUpdateOnlyCheckBox.setSelection(mUpdaterData.getSettingsController().getShowUpdateOnly());\r
124         mUpdateOnlyCheckBox.addSelectionListener(new SelectionAdapter() {\r
125             @Override\r
126             public void widgetSelected(SelectionEvent arg0) {\r
127                 onShowUpdateOnly(); //$hide$\r
128             }\r
129         });\r
130 \r
131         mDescriptionContainer = new Group(parent, SWT.NONE);\r
132         mDescriptionContainer.setLayout(new GridLayout(1, false));\r
133         mDescriptionContainer.setText("Description");\r
134         mDescriptionContainer.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, 5, 1));\r
135 \r
136         mDescriptionLabel = new Label(mDescriptionContainer, SWT.NONE);\r
137         mDescriptionLabel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));\r
138         mDescriptionLabel.setText("Line1\nLine2\nLine3");\r
139 \r
140         mAddSiteButton = new Button(parent, SWT.NONE);\r
141         mAddSiteButton.addSelectionListener(new SelectionAdapter() {\r
142             @Override\r
143             public void widgetSelected(SelectionEvent e) {\r
144                 onAddSiteSelected(); //$hide$\r
145             }\r
146         });\r
147         mAddSiteButton.setText("Add Site...");\r
148 \r
149         mDeleteSiteButton = new Button(parent, SWT.NONE);\r
150         mDeleteSiteButton.addSelectionListener(new SelectionAdapter() {\r
151             @Override\r
152             public void widgetSelected(SelectionEvent e) {\r
153                 onRemoveSiteSelected(); //$hide$\r
154             }\r
155         });\r
156         mDeleteSiteButton.setText("Delete Site...");\r
157 \r
158         mPlaceholder3 = new Label(parent, SWT.NONE);\r
159         mPlaceholder3.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, false, 1, 1));\r
160 \r
161         mRefreshButton = new Button(parent, SWT.NONE);\r
162         mRefreshButton.addSelectionListener(new SelectionAdapter() {\r
163             @Override\r
164             public void widgetSelected(SelectionEvent e) {\r
165                 onRefreshSelected(); //$hide$\r
166             }\r
167         });\r
168         mRefreshButton.setText("Refresh");\r
169 \r
170         mInstallSelectedButton = new Button(parent, SWT.NONE);\r
171         mInstallSelectedButton.addSelectionListener(new SelectionAdapter() {\r
172             @Override\r
173             public void widgetSelected(SelectionEvent e) {\r
174                 onInstallSelectedArchives();  //$hide$\r
175             }\r
176         });\r
177         mInstallSelectedButton.setText("Install Selected");\r
178     }\r
179 \r
180     @Override\r
181     public void dispose() {\r
182         mUpdaterData.removeListener(this);\r
183         super.dispose();\r
184     }\r
185 \r
186     @Override\r
187     protected void checkSubclass() {\r
188         // Disable the check that prevents subclassing of SWT components\r
189     }\r
190 \r
191     // -- Start of internal part ----------\r
192     // Hide everything down-below from SWT designer\r
193     //$hide>>$\r
194 \r
195     /**\r
196      * Called by the constructor right after {@link #createContents(Composite)}.\r
197      */\r
198     private void postCreate() {\r
199         mUpdaterData.addListeners(this);\r
200         adjustColumnsWidth();\r
201         updateButtonsState();\r
202     }\r
203 \r
204     /**\r
205      * Adds a listener to adjust the columns width when the parent is resized.\r
206      * <p/>\r
207      * If we need something more fancy, we might want to use this:\r
208      * http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet77.java?view=co\r
209      */\r
210     private void adjustColumnsWidth() {\r
211         // Add a listener to resize the column to the full width of the table\r
212         ControlAdapter resizer = new ControlAdapter() {\r
213             @Override\r
214             public void controlResized(ControlEvent e) {\r
215                 Rectangle r = mTreeSources.getClientArea();\r
216                 mColumnSource.setWidth(r.width);\r
217             }\r
218         };\r
219 \r
220         mTreeSources.addControlListener(resizer);\r
221         resizer.controlResized(null);\r
222     }\r
223 \r
224     /**\r
225      * Called when an item in the package table viewer is selected.\r
226      * If the items is an {@link IDescription} (as it should), this will display its long\r
227      * description in the description area. Otherwise when the item is not of the expected\r
228      * type or there is no selection, it empties the description area.\r
229      */\r
230     private void onTreeSelected() {\r
231         updateButtonsState();\r
232 \r
233         ISelection sel = mTreeViewerSources.getSelection();\r
234         if (sel instanceof ITreeSelection) {\r
235             Object elem = ((ITreeSelection) sel).getFirstElement();\r
236             if (elem instanceof IDescription) {\r
237                 mDescriptionLabel.setText(((IDescription) elem).getLongDescription());\r
238                 mDescriptionContainer.layout(true);\r
239                 return;\r
240             }\r
241         }\r
242         mDescriptionLabel.setText("");  //$NON-NLS1-$\r
243     }\r
244 \r
245     /**\r
246      * Handle checking and unchecking of the tree items.\r
247      *\r
248      * When unchecking, all sub-tree items checkboxes are cleared too.\r
249      * When checking a source, all of its packages are checked too.\r
250      * When checking a package, only its compatible archives are checked.\r
251      */\r
252     private void onTreeCheckStateChanged(CheckStateChangedEvent event) {\r
253         updateButtonsState();\r
254 \r
255         boolean b = event.getChecked();\r
256         Object elem = event.getElement(); // Will be Archive or Package or RepoSource\r
257 \r
258         assert event.getSource() == mTreeViewerSources;\r
259 \r
260         // when deselecting, we just deselect all children too\r
261         if (b == false) {\r
262             mTreeViewerSources.setSubtreeChecked(elem, b);\r
263             return;\r
264         }\r
265 \r
266         ITreeContentProvider provider =\r
267             (ITreeContentProvider) mTreeViewerSources.getContentProvider();\r
268 \r
269         // When selecting, we want to only select compatible archives.\r
270         if (elem instanceof RepoSource) {\r
271             mTreeViewerSources.setExpandedState(elem, true);\r
272             for (Object pkg : provider.getChildren(elem)) {\r
273                 mTreeViewerSources.setChecked(pkg, true);\r
274                 selectCompatibleArchives(pkg, provider);\r
275             }\r
276         } else if (elem instanceof Package) {\r
277             selectCompatibleArchives(elem, provider);\r
278         }\r
279     }\r
280 \r
281     private void selectCompatibleArchives(Object pkg, ITreeContentProvider provider) {\r
282         mTreeViewerSources.setExpandedState(pkg, true);\r
283         for (Object archive : provider.getChildren(pkg)) {\r
284             if (archive instanceof Archive) {\r
285                 mTreeViewerSources.setChecked(archive, ((Archive) archive).isCompatible());\r
286             }\r
287         }\r
288     }\r
289 \r
290     private void onShowUpdateOnly() {\r
291         mUpdaterData.getSettingsController().setShowUpdateOnly(mUpdateOnlyCheckBox.getSelection());\r
292         mTreeViewerSources.refresh();\r
293     }\r
294 \r
295     private void onInstallSelectedArchives() {\r
296         ArrayList<Archive> archives = new ArrayList<Archive>();\r
297         for (Object element : mTreeViewerSources.getCheckedElements()) {\r
298             if (element instanceof Archive) {\r
299                 archives.add((Archive) element);\r
300             }\r
301         }\r
302 \r
303         if (mUpdaterData != null) {\r
304             mUpdaterData.updateOrInstallAll(archives);\r
305         }\r
306     }\r
307 \r
308     private void onAddSiteSelected() {\r
309 \r
310         final RepoSource[] knowSources = mUpdaterData.getSources().getSources();\r
311         String title = "Add Site URL";\r
312         String msg = "Please enter the URL of the repository.xml for the new site:";\r
313 \r
314         InputDialog dlg = new InputDialog(getShell(), title, msg, null, new IInputValidator() {\r
315             public String isValid(String newText) {\r
316 \r
317                 if (newText == null || newText.length() == 0) {\r
318                     return "Please enter an URL.";\r
319                 }\r
320 \r
321                 // A URL should have one of the following prefixes\r
322                 if (!newText.startsWith("file://") &&\r
323                         !newText.startsWith("ftp://") &&\r
324                         !newText.startsWith("http://") &&\r
325                         !newText.startsWith("https://")) {\r
326                     return "The URL must start by one of file://, ftp://, http:// or https://";\r
327                 }\r
328 \r
329                 // Reject URLs that are already in the source list\r
330                 for (RepoSource s : knowSources) {\r
331                     if (newText.equals(s.getUrl())) {\r
332                         return "This site is already listed.";\r
333                     }\r
334                 }\r
335 \r
336                 return null;\r
337             }\r
338         });\r
339 \r
340         if (dlg.open() == Window.OK) {\r
341             String url = dlg.getValue();\r
342             mUpdaterData.getSources().add(new RepoSource(url, true /*userSource*/));\r
343             onRefreshSelected();\r
344         }\r
345     }\r
346 \r
347     private void onRemoveSiteSelected() {\r
348         boolean changed = false;\r
349 \r
350         ISelection sel = mTreeViewerSources.getSelection();\r
351         if (mUpdaterData != null && sel instanceof ITreeSelection) {\r
352             for (Object c : ((ITreeSelection) sel).toList()) {\r
353                 if (c instanceof RepoSource && ((RepoSource) c).isUserSource()) {\r
354                     RepoSource source = (RepoSource) c;\r
355 \r
356                     String title = "Delete Site?";\r
357 \r
358                     String msg = String.format("Are you sure you want to delete the site '%1$s'?",\r
359                             source.getUrl());\r
360 \r
361                     if (MessageDialog.openQuestion(getShell(), title, msg)) {\r
362                         mUpdaterData.getSources().remove(source);\r
363                         changed = true;\r
364                     }\r
365                 }\r
366             }\r
367         }\r
368 \r
369         if (changed) {\r
370             onRefreshSelected();\r
371         }\r
372     }\r
373 \r
374     private void onRefreshSelected() {\r
375         if (mUpdaterData != null) {\r
376             mUpdaterData.refreshSources(false /*forceFetching*/);\r
377         }\r
378         mTreeViewerSources.refresh();\r
379         updateButtonsState();\r
380     }\r
381 \r
382     public void onSdkChange() {\r
383         RepoSourcesAdapter sources = mUpdaterData.getSourcesAdapter();\r
384         mTreeViewerSources.setContentProvider(sources.getContentProvider());\r
385         mTreeViewerSources.setLabelProvider(  sources.getLabelProvider());\r
386         mTreeViewerSources.setInput(sources);\r
387         onTreeSelected();\r
388     }\r
389 \r
390     private void updateButtonsState() {\r
391         // We install archives, so there should be at least one checked archive.\r
392         // Having sites or packages checked does not count.\r
393         boolean hasCheckedArchive = false;\r
394         Object[] checked = mTreeViewerSources.getCheckedElements();\r
395         if (checked != null) {\r
396             for (Object c : checked) {\r
397                 if (c instanceof Archive) {\r
398                     hasCheckedArchive = true;\r
399                     break;\r
400                 }\r
401             }\r
402         }\r
403 \r
404         // Is there a selected site Source?\r
405         boolean hasSelectedUserSource = false;\r
406         ISelection sel = mTreeViewerSources.getSelection();\r
407         if (sel instanceof ITreeSelection) {\r
408             for (Object c : ((ITreeSelection) sel).toList()) {\r
409                 if (c instanceof RepoSource &&\r
410                         ((RepoSource) c).isUserSource()) {\r
411                     hasSelectedUserSource = true;\r
412                     break;\r
413                 }\r
414             }\r
415         }\r
416 \r
417         mAddSiteButton.setEnabled(true);\r
418         mDeleteSiteButton.setEnabled(hasSelectedUserSource);\r
419         mRefreshButton.setEnabled(true);\r
420         mInstallSelectedButton.setEnabled(hasCheckedArchive);\r
421     }\r
422 \r
423     // End of hiding from SWT Designer\r
424     //$hide<<$\r
425 }\r