OSDN Git Service

original
[gb-231r1-is01/Gingerbread_2.3.3_r1_IS01.git] / sdk / sdkmanager / libs / sdkuilib / src / com / android / sdkuilib / internal / widgets / SdkTargetSelector.java
1 /*
2  * Copyright (C) 2009 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
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.sdkuilib.internal.widgets;
18
19 import com.android.sdklib.IAndroidTarget;
20
21 import org.eclipse.swt.SWT;
22 import org.eclipse.swt.events.ControlAdapter;
23 import org.eclipse.swt.events.ControlEvent;
24 import org.eclipse.swt.events.SelectionEvent;
25 import org.eclipse.swt.events.SelectionListener;
26 import org.eclipse.swt.graphics.Point;
27 import org.eclipse.swt.graphics.Rectangle;
28 import org.eclipse.swt.layout.GridData;
29 import org.eclipse.swt.layout.GridLayout;
30 import org.eclipse.swt.widgets.Composite;
31 import org.eclipse.swt.widgets.Control;
32 import org.eclipse.swt.widgets.Event;
33 import org.eclipse.swt.widgets.Label;
34 import org.eclipse.swt.widgets.Listener;
35 import org.eclipse.swt.widgets.Table;
36 import org.eclipse.swt.widgets.TableColumn;
37 import org.eclipse.swt.widgets.TableItem;
38
39 import java.util.Arrays;
40 import java.util.Comparator;
41
42
43 /**
44  * The SDK target selector is a table that is added to the given parent composite.
45  * <p/>
46  * To use, create it using {@link #SdkTargetSelector(Composite, IAndroidTarget[], boolean)} then
47  * call {@link #setSelection(IAndroidTarget)}, {@link #setSelectionListener(SelectionListener)}
48  * and finally use {@link #getSelected()} to retrieve the
49  * selection.
50  */
51 public class SdkTargetSelector {
52
53     private IAndroidTarget[] mTargets;
54     private final boolean mAllowSelection;
55     private SelectionListener mSelectionListener;
56     private Table mTable;
57     private Label mDescription;
58     private Composite mInnerGroup;
59
60     /**
61      * Creates a new SDK Target Selector.
62      *
63      * @param parent The parent composite where the selector will be added.
64      * @param targets The list of targets. This is <em>not</em> copied, the caller must not modify.
65      *                Targets can be null or an empty array, in which case the table is disabled.
66      */
67     public SdkTargetSelector(Composite parent, IAndroidTarget[] targets) {
68         this(parent, targets, true /*allowSelection*/);
69     }
70
71     /**
72      * Creates a new SDK Target Selector.
73      *
74      * @param parent The parent composite where the selector will be added.
75      * @param targets The list of targets. This is <em>not</em> copied, the caller must not modify.
76      *                Targets can be null or an empty array, in which case the table is disabled.
77      * @param allowSelection True if selection is enabled.
78      */
79     public SdkTargetSelector(Composite parent, IAndroidTarget[] targets, boolean allowSelection) {
80         // Layout has 1 column
81         mInnerGroup = new Composite(parent, SWT.NONE);
82         mInnerGroup.setLayout(new GridLayout());
83         mInnerGroup.setLayoutData(new GridData(GridData.FILL_BOTH));
84         mInnerGroup.setFont(parent.getFont());
85
86         mAllowSelection = allowSelection;
87         int style = SWT.BORDER | SWT.SINGLE | SWT.FULL_SELECTION;
88         if (allowSelection) {
89             style |= SWT.CHECK;
90         }
91         mTable = new Table(mInnerGroup, style);
92         mTable.setHeaderVisible(true);
93         mTable.setLinesVisible(false);
94
95         GridData data = new GridData();
96         data.grabExcessVerticalSpace = true;
97         data.grabExcessHorizontalSpace = true;
98         data.horizontalAlignment = GridData.FILL;
99         data.verticalAlignment = GridData.FILL;
100         mTable.setLayoutData(data);
101
102         mDescription = new Label(mInnerGroup, SWT.WRAP);
103         mDescription.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
104
105         // create the table columns
106         final TableColumn column0 = new TableColumn(mTable, SWT.NONE);
107         column0.setText("Target Name");
108         final TableColumn column1 = new TableColumn(mTable, SWT.NONE);
109         column1.setText("Vendor");
110         final TableColumn column2 = new TableColumn(mTable, SWT.NONE);
111         column2.setText("Platform");
112         final TableColumn column3 = new TableColumn(mTable, SWT.NONE);
113         column3.setText("API Level");
114
115         adjustColumnsWidth(mTable, column0, column1, column2, column3);
116         setupSelectionListener(mTable);
117         setTargets(targets);
118         setupTooltip(mTable);
119     }
120
121     /**
122      * Returns the layout data of the inner composite widget that contains the target selector.
123      * By default the layout data is set to a {@link GridData} with a {@link GridData#FILL_BOTH}
124      * mode.
125      * <p/>
126      * This can be useful if you want to change the {@link GridData#horizontalSpan} for example.
127      */
128     public Object getLayoutData() {
129         return mInnerGroup.getLayoutData();
130     }
131
132     /**
133      * Returns the list of known targets.
134      * <p/>
135      * This is not a copy. Callers must <em>not</em> modify this array.
136      */
137     public IAndroidTarget[] getTargets() {
138         return mTargets;
139     }
140
141     /**
142      * Changes the targets of the SDK Target Selector.
143      *
144      * @param targets The list of targets. This is <em>not</em> copied, the caller must not modify.
145      */
146     public void setTargets(IAndroidTarget[] targets) {
147         mTargets = targets;
148         if (mTargets != null) {
149             Arrays.sort(mTargets, new Comparator<IAndroidTarget>() {
150                 public int compare(IAndroidTarget o1, IAndroidTarget o2) {
151                     return o1.compareTo(o2);
152                 }
153             });
154         }
155
156         fillTable(mTable);
157     }
158
159     /**
160      * Sets a selection listener. Set it to null to remove it.
161      * The listener will be called <em>after</em> this table processed its selection
162      * events so that the caller can see the updated state.
163      * <p/>
164      * The event's item contains a {@link TableItem}.
165      * The {@link TableItem#getData()} contains an {@link IAndroidTarget}.
166      * <p/>
167      * It is recommended that the caller uses the {@link #getSelected()} method instead.
168      *
169      * @param selectionListener The new listener or null to remove it.
170      */
171     public void setSelectionListener(SelectionListener selectionListener) {
172         mSelectionListener = selectionListener;
173     }
174
175     /**
176      * Sets the current target selection.
177      * <p/>
178      * If the selection is actually changed, this will invoke the selection listener
179      * (if any) with a null event.
180      *
181      * @param target the target to be selection
182      * @return true if the target could be selected, false otherwise.
183      */
184     public boolean setSelection(IAndroidTarget target) {
185         if (!mAllowSelection) {
186             return false;
187         }
188
189         boolean found = false;
190         boolean modified = false;
191
192         if (mTable != null && !mTable.isDisposed()) {
193             for (TableItem i : mTable.getItems()) {
194                 if ((IAndroidTarget) i.getData() == target) {
195                     found = true;
196                     if (!i.getChecked()) {
197                         modified = true;
198                         i.setChecked(true);
199                     }
200                 } else if (i.getChecked()) {
201                     modified = true;
202                     i.setChecked(false);
203                 }
204             }
205         }
206
207         if (modified && mSelectionListener != null) {
208             mSelectionListener.widgetSelected(null);
209         }
210
211         return found;
212     }
213
214     /**
215      * Returns the selected item.
216      *
217      * @return The selected item or null.
218      */
219     public IAndroidTarget getSelected() {
220         if (mTable == null || mTable.isDisposed()) {
221             return null;
222         }
223
224         for (TableItem i : mTable.getItems()) {
225             if (i.getChecked()) {
226                 return (IAndroidTarget) i.getData();
227             }
228         }
229         return null;
230     }
231
232     /**
233      * Adds a listener to adjust the columns width when the parent is resized.
234      * <p/>
235      * If we need something more fancy, we might want to use this:
236      * http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet77.java?view=co
237      */
238     private void adjustColumnsWidth(final Table table,
239             final TableColumn column0,
240             final TableColumn column1,
241             final TableColumn column2,
242             final TableColumn column3) {
243         // Add a listener to resize the column to the full width of the table
244         table.addControlListener(new ControlAdapter() {
245             @Override
246             public void controlResized(ControlEvent e) {
247                 Rectangle r = table.getClientArea();
248                 column0.setWidth(r.width * 30 / 100); // 30%
249                 column1.setWidth(r.width * 45 / 100); // 45%
250                 column2.setWidth(r.width * 15 / 100); // 15%
251                 column3.setWidth(r.width * 10 / 100); // 10%
252             }
253         });
254     }
255
256
257     /**
258      * Creates a selection listener that will check or uncheck the whole line when
259      * double-clicked (aka "the default selection").
260      */
261     private void setupSelectionListener(final Table table) {
262         if (!mAllowSelection) {
263             return;
264         }
265
266         // Add a selection listener that will check/uncheck items when they are double-clicked
267         table.addSelectionListener(new SelectionListener() {
268             /** Default selection means double-click on "most" platforms */
269             public void widgetDefaultSelected(SelectionEvent e) {
270                 if (e.item instanceof TableItem) {
271                     TableItem i = (TableItem) e.item;
272                     i.setChecked(!i.getChecked());
273                     enforceSingleSelection(i);
274                     updateDescription(i);
275                 }
276
277                 if (mSelectionListener != null) {
278                     mSelectionListener.widgetDefaultSelected(e);
279                 }
280             }
281
282             public void widgetSelected(SelectionEvent e) {
283                 if (e.item instanceof TableItem) {
284                     TableItem i = (TableItem) e.item;
285                     enforceSingleSelection(i);
286                     updateDescription(i);
287                 }
288
289                 if (mSelectionListener != null) {
290                     mSelectionListener.widgetSelected(e);
291                 }
292             }
293
294             /**
295              * If we're not in multiple selection mode, uncheck all other
296              * items when this one is selected.
297              */
298             private void enforceSingleSelection(TableItem item) {
299                 if (item.getChecked()) {
300                     Table parentTable = item.getParent();
301                     for (TableItem i2 : parentTable.getItems()) {
302                         if (i2 != item && i2.getChecked()) {
303                             i2.setChecked(false);
304                         }
305                     }
306                 }
307             }
308         });
309     }
310
311
312     /**
313      * Fills the table with all SDK targets.
314      * The table columns are:
315      * <ul>
316      * <li>column 0: sdk name
317      * <li>column 1: sdk vendor
318      * <li>column 2: sdk api name
319      * <li>column 3: sdk version
320      * </ul>
321      */
322     private void fillTable(final Table table) {
323
324         if (table == null || table.isDisposed()) {
325             return;
326         }
327
328         table.removeAll();
329
330         if (mTargets != null && mTargets.length > 0) {
331             table.setEnabled(true);
332             for (IAndroidTarget target : mTargets) {
333                 TableItem item = new TableItem(table, SWT.NONE);
334                 item.setData(target);
335                 item.setText(0, target.getName());
336                 item.setText(1, target.getVendor());
337                 item.setText(2, target.getVersionName());
338                 item.setText(3, target.getVersion().getApiString());
339             }
340         } else {
341             table.setEnabled(false);
342             TableItem item = new TableItem(table, SWT.NONE);
343             item.setData(null);
344             item.setText(0, "--");
345             item.setText(1, "No target available");
346             item.setText(2, "--");
347             item.setText(3, "--");
348         }
349     }
350
351     /**
352      * Sets up a tooltip that displays the current item description.
353      * <p/>
354      * Displaying a tooltip over the table looks kind of odd here. Instead we actually
355      * display the description in a label under the table.
356      */
357     private void setupTooltip(final Table table) {
358
359         if (table == null || table.isDisposed()) {
360             return;
361         }
362
363         /*
364          * Reference:
365          * http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet125.java?view=markup
366          */
367
368         final Listener listener = new Listener() {
369             public void handleEvent(Event event) {
370
371                 switch(event.type) {
372                 case SWT.KeyDown:
373                 case SWT.MouseExit:
374                 case SWT.MouseDown:
375                     return;
376
377                 case SWT.MouseHover:
378                     updateDescription(table.getItem(new Point(event.x, event.y)));
379                     break;
380
381                 case SWT.Selection:
382                     if (event.item instanceof TableItem) {
383                         updateDescription((TableItem) event.item);
384                     }
385                     break;
386
387                 default:
388                     return;
389                 }
390
391             }
392         };
393
394         table.addListener(SWT.Dispose, listener);
395         table.addListener(SWT.KeyDown, listener);
396         table.addListener(SWT.MouseMove, listener);
397         table.addListener(SWT.MouseHover, listener);
398     }
399
400     /**
401      * Updates the description label with the description of the item's android target, if any.
402      */
403     private void updateDescription(TableItem item) {
404         if (item != null) {
405             Object data = item.getData();
406             if (data instanceof IAndroidTarget) {
407                 String newTooltip = ((IAndroidTarget) data).getDescription();
408                 mDescription.setText(newTooltip == null ? "" : newTooltip);  //$NON-NLS-1$
409             }
410         }
411     }
412
413     /** Enables or disables the controls. */
414     public void setEnabled(boolean enabled) {
415         if (mInnerGroup != null && mTable != null && !mTable.isDisposed()) {
416             enableControl(mInnerGroup, enabled);
417         }
418     }
419
420     /** Enables or disables controls; recursive for composite controls. */
421     private void enableControl(Control c, boolean enabled) {
422         c.setEnabled(enabled);
423         if (c instanceof Composite)
424         for (Control c2 : ((Composite) c).getChildren()) {
425             enableControl(c2, enabled);
426         }
427     }
428
429 }