OSDN Git Service

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