2 * Copyright (C) 2009 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.sdkuilib.internal.widgets;
19 import com.android.sdklib.IAndroidTarget;
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;
39 import java.util.Arrays;
40 import java.util.Comparator;
44 * The SDK target selector is a table that is added to the given parent composite.
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
51 public class SdkTargetSelector {
53 private IAndroidTarget[] mTargets;
54 private final boolean mAllowSelection;
55 private SelectionListener mSelectionListener;
57 private Label mDescription;
58 private Composite mInnerGroup;
61 * Creates a new SDK Target Selector.
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.
67 public SdkTargetSelector(Composite parent, IAndroidTarget[] targets) {
68 this(parent, targets, true /*allowSelection*/);
72 * Creates a new SDK Target Selector.
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.
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());
86 mAllowSelection = allowSelection;
87 int style = SWT.BORDER | SWT.SINGLE | SWT.FULL_SELECTION;
91 mTable = new Table(mInnerGroup, style);
92 mTable.setHeaderVisible(true);
93 mTable.setLinesVisible(false);
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);
102 mDescription = new Label(mInnerGroup, SWT.WRAP);
103 mDescription.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
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");
115 adjustColumnsWidth(mTable, column0, column1, column2, column3);
116 setupSelectionListener(mTable);
118 setupTooltip(mTable);
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}
126 * This can be useful if you want to change the {@link GridData#horizontalSpan} for example.
128 public Object getLayoutData() {
129 return mInnerGroup.getLayoutData();
133 * Returns the list of known targets.
135 * This is not a copy. Callers must <em>not</em> modify this array.
137 public IAndroidTarget[] getTargets() {
142 * Changes the targets of the SDK Target Selector.
144 * @param targets The list of targets. This is <em>not</em> copied, the caller must not modify.
146 public void setTargets(IAndroidTarget[] 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);
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.
164 * The event's item contains a {@link TableItem}.
165 * The {@link TableItem#getData()} contains an {@link IAndroidTarget}.
167 * It is recommended that the caller uses the {@link #getSelected()} method instead.
169 * @param selectionListener The new listener or null to remove it.
171 public void setSelectionListener(SelectionListener selectionListener) {
172 mSelectionListener = selectionListener;
176 * Sets the current target selection.
178 * If the selection is actually changed, this will invoke the selection listener
179 * (if any) with a null event.
181 * @param target the target to be selection
182 * @return true if the target could be selected, false otherwise.
184 public boolean setSelection(IAndroidTarget target) {
185 if (!mAllowSelection) {
189 boolean found = false;
190 boolean modified = false;
192 if (mTable != null && !mTable.isDisposed()) {
193 for (TableItem i : mTable.getItems()) {
194 if ((IAndroidTarget) i.getData() == target) {
196 if (!i.getChecked()) {
200 } else if (i.getChecked()) {
207 if (modified && mSelectionListener != null) {
208 mSelectionListener.widgetSelected(null);
215 * Returns the selected item.
217 * @return The selected item or null.
219 public IAndroidTarget getSelected() {
220 if (mTable == null || mTable.isDisposed()) {
224 for (TableItem i : mTable.getItems()) {
225 if (i.getChecked()) {
226 return (IAndroidTarget) i.getData();
233 * Adds a listener to adjust the columns width when the parent is resized.
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
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() {
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%
258 * Creates a selection listener that will check or uncheck the whole line when
259 * double-clicked (aka "the default selection").
261 private void setupSelectionListener(final Table table) {
262 if (!mAllowSelection) {
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);
277 if (mSelectionListener != null) {
278 mSelectionListener.widgetDefaultSelected(e);
282 public void widgetSelected(SelectionEvent e) {
283 if (e.item instanceof TableItem) {
284 TableItem i = (TableItem) e.item;
285 enforceSingleSelection(i);
286 updateDescription(i);
289 if (mSelectionListener != null) {
290 mSelectionListener.widgetSelected(e);
295 * If we're not in multiple selection mode, uncheck all other
296 * items when this one is selected.
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);
313 * Fills the table with all SDK targets.
314 * The table columns are:
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
322 private void fillTable(final Table table) {
324 if (table == null || table.isDisposed()) {
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());
341 table.setEnabled(false);
342 TableItem item = new TableItem(table, SWT.NONE);
344 item.setText(0, "--");
345 item.setText(1, "No target available");
346 item.setText(2, "--");
347 item.setText(3, "--");
352 * Sets up a tooltip that displays the current item description.
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.
357 private void setupTooltip(final Table table) {
359 if (table == null || table.isDisposed()) {
365 * http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet125.java?view=markup
368 final Listener listener = new Listener() {
369 public void handleEvent(Event event) {
378 updateDescription(table.getItem(new Point(event.x, event.y)));
382 if (event.item instanceof TableItem) {
383 updateDescription((TableItem) event.item);
394 table.addListener(SWT.Dispose, listener);
395 table.addListener(SWT.KeyDown, listener);
396 table.addListener(SWT.MouseMove, listener);
397 table.addListener(SWT.MouseHover, listener);
401 * Updates the description label with the description of the item's android target, if any.
403 private void updateDescription(TableItem item) {
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$
413 /** Enables or disables the controls. */
414 public void setEnabled(boolean enabled) {
415 if (mInnerGroup != null && mTable != null && !mTable.isDisposed()) {
416 enableControl(mInnerGroup, enabled);
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);