1/*
2 * Copyright (C) 2010 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
17package com.android.hierarchyviewerlib.ui;
18
19import com.android.ddmlib.IDevice;
20import com.android.ddmuilib.ImageLoader;
21import com.android.hierarchyviewerlib.HierarchyViewerDirector;
22import com.android.hierarchyviewerlib.device.Window;
23import com.android.hierarchyviewerlib.models.DeviceSelectionModel;
24import com.android.hierarchyviewerlib.models.DeviceSelectionModel.IWindowChangeListener;
25
26import org.eclipse.jface.viewers.IFontProvider;
27import org.eclipse.jface.viewers.ILabelProvider;
28import org.eclipse.jface.viewers.ILabelProviderListener;
29import org.eclipse.jface.viewers.ITreeContentProvider;
30import org.eclipse.jface.viewers.TreeSelection;
31import org.eclipse.jface.viewers.TreeViewer;
32import org.eclipse.jface.viewers.Viewer;
33import org.eclipse.swt.SWT;
34import org.eclipse.swt.events.ControlAdapter;
35import org.eclipse.swt.events.ControlEvent;
36import org.eclipse.swt.events.ControlListener;
37import org.eclipse.swt.events.DisposeEvent;
38import org.eclipse.swt.events.DisposeListener;
39import org.eclipse.swt.events.SelectionEvent;
40import org.eclipse.swt.events.SelectionListener;
41import org.eclipse.swt.graphics.Font;
42import org.eclipse.swt.graphics.FontData;
43import org.eclipse.swt.graphics.Image;
44import org.eclipse.swt.layout.FillLayout;
45import org.eclipse.swt.widgets.Composite;
46import org.eclipse.swt.widgets.Display;
47import org.eclipse.swt.widgets.Tree;
48import org.eclipse.swt.widgets.TreeItem;
49
50public class DeviceSelector extends Composite implements IWindowChangeListener, SelectionListener {
51    private TreeViewer mTreeViewer;
52
53    private Tree mTree;
54
55    private DeviceSelectionModel mModel;
56
57    private Font mBoldFont;
58
59    private Image mDeviceImage;
60
61    private Image mEmulatorImage;
62
63    private final static int ICON_WIDTH = 16;
64
65    private boolean mDoTreeViewStuff;
66
67    private boolean mDoPixelPerfectStuff;
68
69    private class ContentProvider implements ITreeContentProvider, ILabelProvider, IFontProvider {
70        @Override
71        public Object[] getChildren(Object parentElement) {
72            if (parentElement instanceof IDevice && mDoTreeViewStuff) {
73                Window[] list = mModel.getWindows((IDevice) parentElement);
74                if (list != null) {
75                    return list;
76                }
77            }
78            return new Object[0];
79        }
80
81        @Override
82        public Object getParent(Object element) {
83            if (element instanceof Window) {
84                return ((Window) element).getDevice();
85            }
86            return null;
87        }
88
89        @Override
90        public boolean hasChildren(Object element) {
91            if (element instanceof IDevice && mDoTreeViewStuff) {
92                Window[] list = mModel.getWindows((IDevice) element);
93                if (list != null) {
94                    return list.length != 0;
95                }
96            }
97            return false;
98        }
99
100        @Override
101        public Object[] getElements(Object inputElement) {
102            if (inputElement instanceof DeviceSelectionModel) {
103                return mModel.getDevices();
104            }
105            return new Object[0];
106        }
107
108        @Override
109        public void dispose() {
110            // pass
111        }
112
113        @Override
114        public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
115            // pass
116        }
117
118        @Override
119        public Image getImage(Object element) {
120            if (element instanceof IDevice) {
121                if (((IDevice) element).isEmulator()) {
122                    return mEmulatorImage;
123                }
124                return mDeviceImage;
125            }
126            return null;
127        }
128
129        @Override
130        public String getText(Object element) {
131            if (element instanceof IDevice) {
132                return ((IDevice) element).toString();
133            } else if (element instanceof Window) {
134                return ((Window) element).getTitle();
135            }
136            return null;
137        }
138
139        @Override
140        public Font getFont(Object element) {
141            if (element instanceof Window) {
142                int focusedWindow = mModel.getFocusedWindow(((Window) element).getDevice());
143                if (focusedWindow == ((Window) element).getHashCode()) {
144                    return mBoldFont;
145                }
146            }
147            return null;
148        }
149
150        @Override
151        public void addListener(ILabelProviderListener listener) {
152            // pass
153        }
154
155        @Override
156        public boolean isLabelProperty(Object element, String property) {
157            // pass
158            return false;
159        }
160
161        @Override
162        public void removeListener(ILabelProviderListener listener) {
163            // pass
164        }
165    }
166
167    public DeviceSelector(Composite parent, boolean doTreeViewStuff, boolean doPixelPerfectStuff) {
168        super(parent, SWT.NONE);
169        this.mDoTreeViewStuff = doTreeViewStuff;
170        this.mDoPixelPerfectStuff = doPixelPerfectStuff;
171        setLayout(new FillLayout());
172        mTreeViewer = new TreeViewer(this, SWT.SINGLE);
173        mTreeViewer.setAutoExpandLevel(TreeViewer.ALL_LEVELS);
174
175        mTree = mTreeViewer.getTree();
176        mTree.setLinesVisible(true);
177        mTree.addSelectionListener(this);
178
179        addDisposeListener(mDisposeListener);
180
181        loadResources();
182
183        mModel = DeviceSelectionModel.getModel();
184        ContentProvider contentProvider = new ContentProvider();
185        mTreeViewer.setContentProvider(contentProvider);
186        mTreeViewer.setLabelProvider(contentProvider);
187        mModel.addWindowChangeListener(this);
188        mTreeViewer.setInput(mModel);
189
190        addControlListener(mControlListener);
191    }
192
193    public void loadResources() {
194        Display display = Display.getDefault();
195        Font systemFont = display.getSystemFont();
196        FontData[] fontData = systemFont.getFontData();
197        FontData[] newFontData = new FontData[fontData.length];
198        for (int i = 0; i < fontData.length; i++) {
199            newFontData[i] =
200                    new FontData(fontData[i].getName(), fontData[i].getHeight(), fontData[i]
201                            .getStyle()
202                            | SWT.BOLD);
203        }
204        mBoldFont = new Font(Display.getDefault(), newFontData);
205
206        ImageLoader loader = ImageLoader.getDdmUiLibLoader();
207        mDeviceImage =
208                loader.loadImage(display, "device.png", ICON_WIDTH, ICON_WIDTH, display //$NON-NLS-1$
209                        .getSystemColor(SWT.COLOR_RED));
210
211        mEmulatorImage =
212                loader.loadImage(display, "emulator.png", ICON_WIDTH, ICON_WIDTH, display //$NON-NLS-1$
213                        .getSystemColor(SWT.COLOR_BLUE));
214    }
215
216    private DisposeListener mDisposeListener = new DisposeListener() {
217        @Override
218        public void widgetDisposed(DisposeEvent e) {
219            mModel.removeWindowChangeListener(DeviceSelector.this);
220            mBoldFont.dispose();
221        }
222    };
223
224    // If the window gets too small, hide the data, otherwise SWT throws an
225    // ERROR.
226
227    private ControlListener mControlListener = new ControlAdapter() {
228        private boolean noInput = false;
229
230        @Override
231        public void controlResized(ControlEvent e) {
232            if (getBounds().height <= 38) {
233                mTreeViewer.setInput(null);
234                noInput = true;
235            } else if (noInput) {
236                mTreeViewer.setInput(mModel);
237                noInput = false;
238            }
239        }
240    };
241
242    @Override
243    public boolean setFocus() {
244        return mTree.setFocus();
245    }
246
247    public void setMode(boolean doTreeViewStuff, boolean doPixelPerfectStuff) {
248        if (this.mDoTreeViewStuff != doTreeViewStuff
249                || this.mDoPixelPerfectStuff != doPixelPerfectStuff) {
250            final boolean expandAll = !this.mDoTreeViewStuff && doTreeViewStuff;
251            this.mDoTreeViewStuff = doTreeViewStuff;
252            this.mDoPixelPerfectStuff = doPixelPerfectStuff;
253            Display.getDefault().syncExec(new Runnable() {
254                @Override
255                public void run() {
256                    mTreeViewer.refresh();
257                    if (expandAll) {
258                        mTreeViewer.expandAll();
259                    }
260                }
261            });
262        }
263    }
264
265    @Override
266    public void deviceConnected(final IDevice device) {
267        Display.getDefault().syncExec(new Runnable() {
268            @Override
269            public void run() {
270                mTreeViewer.refresh();
271                mTreeViewer.setExpandedState(device, true);
272            }
273        });
274    }
275
276    @Override
277    public void deviceChanged(final IDevice device) {
278        Display.getDefault().syncExec(new Runnable() {
279            @Override
280            public void run() {
281                TreeSelection selection = (TreeSelection) mTreeViewer.getSelection();
282                mTreeViewer.refresh(device);
283                if (selection.getFirstElement() instanceof Window
284                        && ((Window) selection.getFirstElement()).getDevice() == device) {
285                    mTreeViewer.setSelection(selection, true);
286                }
287            }
288        });
289    }
290
291    @Override
292    public void deviceDisconnected(final IDevice device) {
293        Display.getDefault().syncExec(new Runnable() {
294            @Override
295            public void run() {
296                mTreeViewer.refresh();
297            }
298        });
299    }
300
301    @Override
302    public void focusChanged(final IDevice device) {
303        Display.getDefault().syncExec(new Runnable() {
304            @Override
305            public void run() {
306                TreeSelection selection = (TreeSelection) mTreeViewer.getSelection();
307                mTreeViewer.refresh(device);
308                if (selection.getFirstElement() instanceof Window
309                        && ((Window) selection.getFirstElement()).getDevice() == device) {
310                    mTreeViewer.setSelection(selection, true);
311                }
312            }
313        });
314    }
315
316    @Override
317    public void selectionChanged(IDevice device, Window window) {
318        // pass
319    }
320
321    @Override
322    public void widgetDefaultSelected(SelectionEvent e) {
323        Object selection = ((TreeItem) e.item).getData();
324        if (selection instanceof IDevice && mDoPixelPerfectStuff) {
325            HierarchyViewerDirector.getDirector().loadPixelPerfectData((IDevice) selection);
326        } else if (selection instanceof Window && mDoTreeViewStuff) {
327            HierarchyViewerDirector.getDirector().loadViewTreeData((Window) selection);
328        }
329    }
330
331    @Override
332    public void widgetSelected(SelectionEvent e) {
333        Object selection = ((TreeItem) e.item).getData();
334        if (selection instanceof IDevice) {
335            mModel.setSelection((IDevice) selection, null);
336        } else if (selection instanceof Window) {
337            mModel.setSelection(((Window) selection).getDevice(), (Window) selection);
338        }
339    }
340}
341