2 * Copyright (C) 2007 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.ddms;
19 import com.android.ddmlib.AndroidDebugBridge;
20 import com.android.ddmlib.Client;
21 import com.android.ddmlib.ClientData;
22 import com.android.ddmlib.IDevice;
23 import com.android.ddmlib.Log;
24 import com.android.ddmlib.SyncException;
25 import com.android.ddmlib.SyncService;
26 import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener;
27 import com.android.ddmlib.ClientData.IHprofDumpHandler;
28 import com.android.ddmlib.ClientData.MethodProfilingStatus;
29 import com.android.ddmlib.Log.ILogOutput;
30 import com.android.ddmlib.Log.LogLevel;
31 import com.android.ddmuilib.AllocationPanel;
32 import com.android.ddmuilib.DevicePanel;
33 import com.android.ddmuilib.EmulatorControlPanel;
34 import com.android.ddmuilib.HeapPanel;
35 import com.android.ddmuilib.ITableFocusListener;
36 import com.android.ddmuilib.ImageLoader;
37 import com.android.ddmuilib.InfoPanel;
38 import com.android.ddmuilib.NativeHeapPanel;
39 import com.android.ddmuilib.ScreenShotDialog;
40 import com.android.ddmuilib.SysinfoPanel;
41 import com.android.ddmuilib.TablePanel;
42 import com.android.ddmuilib.ThreadPanel;
43 import com.android.ddmuilib.DevicePanel.IUiSelectionListener;
44 import com.android.ddmuilib.actions.ToolItemAction;
45 import com.android.ddmuilib.explorer.DeviceExplorer;
46 import com.android.ddmuilib.handler.BaseFileHandler;
47 import com.android.ddmuilib.handler.MethodProfilingHandler;
48 import com.android.ddmuilib.log.event.EventLogPanel;
49 import com.android.ddmuilib.logcat.LogColors;
50 import com.android.ddmuilib.logcat.LogFilter;
51 import com.android.ddmuilib.logcat.LogPanel;
52 import com.android.ddmuilib.logcat.LogPanel.ILogFilterStorageManager;
54 import org.eclipse.jface.dialogs.MessageDialog;
55 import org.eclipse.jface.preference.IPreferenceStore;
56 import org.eclipse.jface.preference.PreferenceStore;
57 import org.eclipse.swt.SWT;
58 import org.eclipse.swt.SWTError;
59 import org.eclipse.swt.SWTException;
60 import org.eclipse.swt.dnd.Clipboard;
61 import org.eclipse.swt.events.ControlEvent;
62 import org.eclipse.swt.events.ControlListener;
63 import org.eclipse.swt.events.MenuAdapter;
64 import org.eclipse.swt.events.MenuEvent;
65 import org.eclipse.swt.events.SelectionAdapter;
66 import org.eclipse.swt.events.SelectionEvent;
67 import org.eclipse.swt.events.ShellEvent;
68 import org.eclipse.swt.events.ShellListener;
69 import org.eclipse.swt.graphics.Color;
70 import org.eclipse.swt.graphics.Font;
71 import org.eclipse.swt.graphics.FontData;
72 import org.eclipse.swt.graphics.Image;
73 import org.eclipse.swt.graphics.Rectangle;
74 import org.eclipse.swt.layout.FillLayout;
75 import org.eclipse.swt.layout.FormAttachment;
76 import org.eclipse.swt.layout.FormData;
77 import org.eclipse.swt.layout.FormLayout;
78 import org.eclipse.swt.layout.GridData;
79 import org.eclipse.swt.layout.GridLayout;
80 import org.eclipse.swt.widgets.Composite;
81 import org.eclipse.swt.widgets.Display;
82 import org.eclipse.swt.widgets.Event;
83 import org.eclipse.swt.widgets.Label;
84 import org.eclipse.swt.widgets.Listener;
85 import org.eclipse.swt.widgets.Menu;
86 import org.eclipse.swt.widgets.MenuItem;
87 import org.eclipse.swt.widgets.MessageBox;
88 import org.eclipse.swt.widgets.Sash;
89 import org.eclipse.swt.widgets.Shell;
90 import org.eclipse.swt.widgets.TabFolder;
91 import org.eclipse.swt.widgets.TabItem;
92 import org.eclipse.swt.widgets.ToolBar;
93 import org.eclipse.swt.widgets.ToolItem;
96 import java.util.ArrayList;
99 * This acts as the UI builder. This cannot be its own thread since this prevent using AWT in an
100 * SWT application. So this class mainly builds the ui, and manages communication between the panels
101 * when {@link IDevice} / {@link Client} selection changes.
103 public class UIThread implements IUiSelectionListener, IClientChangeListener {
105 * UI tab panel definitions. The constants here must match up with the array
106 * indices in mPanels. PANEL_CLIENT_LIST is a "virtual" panel representing
109 public static final int PANEL_CLIENT_LIST = -1;
111 public static final int PANEL_INFO = 0;
113 public static final int PANEL_THREAD = 1;
115 public static final int PANEL_HEAP = 2;
117 public static final int PANEL_NATIVE_HEAP = 3;
119 private static final int PANEL_ALLOCATIONS = 4;
121 private static final int PANEL_SYSINFO = 5;
123 private static final int PANEL_COUNT = 6;
125 /** Content is setup in the constructor */
126 private static TablePanel[] mPanels = new TablePanel[PANEL_COUNT];
128 private static final String[] mPanelNames = new String[] {
129 "Info", "Threads", "VM Heap", "Native Heap", "Allocation Tracker", "Sysinfo"
132 private static final String[] mPanelTips = new String[] {
133 "Client information", "Thread status", "VM heap status",
134 "Native heap status", "Allocation Tracker", "Sysinfo graphs"
137 private static final String PREFERENCE_LOGSASH =
138 "logSashLocation"; //$NON-NLS-1$
139 private static final String PREFERENCE_SASH =
140 "sashLocation"; //$NON-NLS-1$
142 private static final String PREFS_COL_TIME =
143 "logcat.time"; //$NON-NLS-1$
144 private static final String PREFS_COL_LEVEL =
145 "logcat.level"; //$NON-NLS-1$
146 private static final String PREFS_COL_PID =
147 "logcat.pid"; //$NON-NLS-1$
148 private static final String PREFS_COL_TAG =
149 "logcat.tag"; //$NON-NLS-1$
150 private static final String PREFS_COL_MESSAGE =
151 "logcat.message"; //$NON-NLS-1$
153 private static final String PREFS_FILTERS = "logcat.filter"; //$NON-NLS-1$
155 // singleton instance
156 private static UIThread mInstance = new UIThread();
159 private Display mDisplay;
161 // the table we show in the left-hand pane
162 private DevicePanel mDevicePanel;
164 private IDevice mCurrentDevice = null;
165 private Client mCurrentClient = null;
167 // status line at the bottom of the app window
168 private Label mStatusLine;
170 // some toolbar items we need to update
171 private ToolItem mTBShowThreadUpdates;
172 private ToolItem mTBShowHeapUpdates;
173 private ToolItem mTBHalt;
174 private ToolItem mTBCauseGc;
175 private ToolItem mTBDumpHprof;
176 private ToolItem mTBProfiling;
178 private final class FilterStorage implements ILogFilterStorageManager {
180 public LogFilter[] getFilterFromStore() {
181 String filterPrefs = PrefsDialog.getStore().getString(
184 // split in a string per filter
185 String[] filters = filterPrefs.split("\\|"); //$NON-NLS-1$
187 ArrayList<LogFilter> list =
188 new ArrayList<LogFilter>(filters.length);
190 for (String f : filters) {
191 if (f.length() > 0) {
192 LogFilter logFilter = new LogFilter();
193 if (logFilter.loadFromString(f)) {
199 return list.toArray(new LogFilter[list.size()]);
202 public void saveFilters(LogFilter[] filters) {
203 StringBuilder sb = new StringBuilder();
204 for (LogFilter f : filters) {
205 String filterString = f.toString();
206 sb.append(filterString);
210 PrefsDialog.getStore().setValue(PREFS_FILTERS, sb.toString());
213 public boolean requiresDefaultFilter() {
219 private LogPanel mLogPanel;
221 private ToolItemAction mCreateFilterAction;
222 private ToolItemAction mDeleteFilterAction;
223 private ToolItemAction mEditFilterAction;
224 private ToolItemAction mExportAction;
225 private ToolItemAction mClearAction;
227 private ToolItemAction[] mLogLevelActions;
228 private String[] mLogLevelIcons = {
229 "v.png", //$NON-NLS-1S
230 "d.png", //$NON-NLS-1S
231 "i.png", //$NON-NLS-1S
232 "w.png", //$NON-NLS-1S
233 "e.png", //$NON-NLS-1S
236 protected Clipboard mClipboard;
238 private MenuItem mCopyMenuItem;
240 private MenuItem mSelectAllMenuItem;
242 private TableFocusListener mTableListener;
244 private DeviceExplorer mExplorer = null;
245 private Shell mExplorerShell = null;
247 private EmulatorControlPanel mEmulatorPanel;
249 private EventLogPanel mEventLogPanel;
251 private Image mTracingStartImage;
253 private Image mTracingStopImage;
255 private ImageLoader mDdmUiLibLoader;
257 private class TableFocusListener implements ITableFocusListener {
259 private IFocusedTableActivator mCurrentActivator;
261 public void focusGained(IFocusedTableActivator activator) {
262 mCurrentActivator = activator;
263 if (mCopyMenuItem.isDisposed() == false) {
264 mCopyMenuItem.setEnabled(true);
265 mSelectAllMenuItem.setEnabled(true);
269 public void focusLost(IFocusedTableActivator activator) {
270 // if we move from one table to another, it's unclear
271 // if the old table lose its focus before the new
272 // one gets the focus, so we need to check.
273 if (activator == mCurrentActivator) {
275 if (mCopyMenuItem.isDisposed() == false) {
276 mCopyMenuItem.setEnabled(false);
277 mSelectAllMenuItem.setEnabled(false);
282 public void copy(Clipboard clipboard) {
283 if (mCurrentActivator != null) {
284 mCurrentActivator.copy(clipboard);
288 public void selectAll() {
289 if (mCurrentActivator != null) {
290 mCurrentActivator.selectAll();
296 * Handler for HPROF dumps.
297 * This will always prompt the user to save the HPROF file.
299 private class HProfHandler extends BaseFileHandler implements IHprofDumpHandler {
301 public HProfHandler(Shell parentShell) {
305 public void onEndFailure(final Client client, final String message) {
306 mDisplay.asyncExec(new Runnable() {
309 displayErrorFromUiThread(
310 "Unable to create HPROF file for application '%1$s'\n\n%2$s" +
311 "Check logcat for more information.",
312 client.getClientData().getClientDescription(),
313 message != null ? message + "\n\n" : "");
315 // this will make sure the dump hprof button is re-enabled for the
316 // current selection. as the client is finished dumping an hprof file
323 public void onSuccess(final String remoteFilePath, final Client client) {
324 mDisplay.asyncExec(new Runnable() {
326 final IDevice device = client.getDevice();
328 // get the sync service to pull the HPROF file
329 final SyncService sync = client.getDevice().getSyncService();
332 client.getClientData().getClientDescription() + ".hprof",
333 remoteFilePath, "Save HPROF file");
335 displayErrorFromUiThread(
336 "Unable to download HPROF file from device '%1$s'.",
337 device.getSerialNumber());
339 } catch (SyncException e) {
340 if (e.wasCanceled() == false) {
341 displayErrorFromUiThread(
342 "Unable to download HPROF file from device '%1$s'.\n\n%2$s",
343 device.getSerialNumber(), e.getMessage());
345 } catch (Exception e) {
346 displayErrorFromUiThread("Unable to download HPROF file from device '%1$s'.",
347 device.getSerialNumber());
350 // this will make sure the dump hprof button is re-enabled for the
351 // current selection. as the client is finished dumping an hprof file
358 public void onSuccess(final byte[] data, final Client client) {
359 mDisplay.asyncExec(new Runnable() {
361 promptAndSave(client.getClientData().getClientDescription() + ".hprof", data,
368 protected String getDialogTitle() {
369 return "HPROF Error";
375 * Generic constructor.
378 mPanels[PANEL_INFO] = new InfoPanel();
379 mPanels[PANEL_THREAD] = new ThreadPanel();
380 mPanels[PANEL_HEAP] = new HeapPanel();
381 if (PrefsDialog.getStore().getBoolean(PrefsDialog.SHOW_NATIVE_HEAP)) {
382 mPanels[PANEL_NATIVE_HEAP] = new NativeHeapPanel();
384 mPanels[PANEL_NATIVE_HEAP] = null;
386 mPanels[PANEL_ALLOCATIONS] = new AllocationPanel();
387 mPanels[PANEL_SYSINFO] = new SysinfoPanel();
391 * Get singleton instance of the UI thread.
393 public static UIThread getInstance() {
398 * Return the Display. Don't try this unless you're in the UI thread.
400 public Display getDisplay() {
404 public void asyncExec(Runnable r) {
405 if (mDisplay != null && mDisplay.isDisposed() == false) {
406 mDisplay.asyncExec(r);
410 /** returns the IPreferenceStore */
411 public IPreferenceStore getStore() {
412 return PrefsDialog.getStore();
416 * Create SWT objects and drive the user interface event loop.
417 * @param location location of the folder that contains ddms.
419 public void runUI(String ddmsParentLocation) {
420 Display.setAppName("ddms");
421 mDisplay = new Display();
422 final Shell shell = new Shell(mDisplay);
424 // create the image loaders for DDMS and DDMUILIB
425 mDdmUiLibLoader = ImageLoader.getDdmUiLibLoader();
427 shell.setImage(ImageLoader.getLoader(this.getClass()).loadImage(mDisplay,
428 "ddms-icon.png", //$NON-NLS-1$
431 Log.setLogOutput(new ILogOutput() {
432 public void printAndPromptLog(final LogLevel logLevel, final String tag,
433 final String message) {
434 Log.printLog(logLevel, tag, message);
435 // dialog box only run in UI thread..
436 mDisplay.asyncExec(new Runnable() {
438 Shell shell = mDisplay.getActiveShell();
439 if (logLevel == LogLevel.ERROR) {
440 MessageDialog.openError(shell, tag, message);
442 MessageDialog.openWarning(shell, tag, message);
448 public void printLog(LogLevel logLevel, String tag, String message) {
449 Log.printLog(logLevel, tag, message);
453 // set the handler for hprof dump
454 ClientData.setHprofDumpHandler(new HProfHandler(shell));
455 ClientData.setMethodProfilingHandler(new MethodProfilingHandler(shell));
457 // [try to] ensure ADB is running
458 // in the new SDK, adb is in the platform-tools, but when run from the command line
459 // in the Android source tree, then adb is next to ddms.
461 if (ddmsParentLocation != null && ddmsParentLocation.length() != 0) {
462 // check if there's a platform-tools folder
463 File platformTools = new File(new File(ddmsParentLocation).getParent(),
464 "platform-tools"); //$NON-NLS-1$
465 if (platformTools.isDirectory()) {
466 adbLocation = platformTools.getAbsolutePath() + File.separator + "adb"; //$NON-NLS-1$
468 adbLocation = ddmsParentLocation + File.separator + "adb"; //$NON-NLS-1$
471 adbLocation = "adb"; //$NON-NLS-1$
474 AndroidDebugBridge.init(true /* debugger support */);
475 AndroidDebugBridge.createBridge(adbLocation, true /* forceNewBridge */);
477 // we need to listen to client change to be notified of client status (profiling) change
478 AndroidDebugBridge.addClientChangeListener(this);
480 shell.setText("Dalvik Debug Monitor");
481 setConfirmClose(shell);
483 createWidgets(shell);
486 setSizeAndPosition(shell);
489 Log.d("ddms", "UI is up");
491 while (!shell.isDisposed()) {
492 if (!mDisplay.readAndDispatch())
495 mLogPanel.stopLogCat(true);
497 mDevicePanel.dispose();
498 for (TablePanel panel : mPanels) {
504 ImageLoader.dispose();
507 Log.d("ddms", "UI is down");
511 * Set the size and position of the main window from the preference, and
512 * setup listeners for control events (resize/move of the window)
514 private void setSizeAndPosition(final Shell shell) {
515 shell.setMinimumSize(400, 200);
517 // get the x/y and w/h from the prefs
518 PreferenceStore prefs = PrefsDialog.getStore();
519 int x = prefs.getInt(PrefsDialog.SHELL_X);
520 int y = prefs.getInt(PrefsDialog.SHELL_Y);
521 int w = prefs.getInt(PrefsDialog.SHELL_WIDTH);
522 int h = prefs.getInt(PrefsDialog.SHELL_HEIGHT);
524 // check that we're not out of the display area
525 Rectangle rect = mDisplay.getClientArea();
526 // first check the width/height
527 if (w > rect.width) {
529 prefs.setValue(PrefsDialog.SHELL_WIDTH, rect.width);
531 if (h > rect.height) {
533 prefs.setValue(PrefsDialog.SHELL_HEIGHT, rect.height);
535 // then check x. Make sure the left corner is in the screen
538 prefs.setValue(PrefsDialog.SHELL_X, rect.x);
539 } else if (x >= rect.x + rect.width) {
540 x = rect.x + rect.width - w;
541 prefs.setValue(PrefsDialog.SHELL_X, rect.x);
543 // then check y. Make sure the left corner is in the screen
546 prefs.setValue(PrefsDialog.SHELL_Y, rect.y);
547 } else if (y >= rect.y + rect.height) {
548 y = rect.y + rect.height - h;
549 prefs.setValue(PrefsDialog.SHELL_Y, rect.y);
552 // now we can set the location/size
553 shell.setBounds(x, y, w, h);
555 // add listener for resize/move
556 shell.addControlListener(new ControlListener() {
557 public void controlMoved(ControlEvent e) {
559 Rectangle rect = shell.getBounds();
560 // store in pref file
561 PreferenceStore prefs = PrefsDialog.getStore();
562 prefs.setValue(PrefsDialog.SHELL_X, rect.x);
563 prefs.setValue(PrefsDialog.SHELL_Y, rect.y);
566 public void controlResized(ControlEvent e) {
568 Rectangle rect = shell.getBounds();
569 // store in pref file
570 PreferenceStore prefs = PrefsDialog.getStore();
571 prefs.setValue(PrefsDialog.SHELL_WIDTH, rect.width);
572 prefs.setValue(PrefsDialog.SHELL_HEIGHT, rect.height);
578 * Set the size and position of the file explorer window from the
579 * preference, and setup listeners for control events (resize/move of
582 private void setExplorerSizeAndPosition(final Shell shell) {
583 shell.setMinimumSize(400, 200);
585 // get the x/y and w/h from the prefs
586 PreferenceStore prefs = PrefsDialog.getStore();
587 int x = prefs.getInt(PrefsDialog.EXPLORER_SHELL_X);
588 int y = prefs.getInt(PrefsDialog.EXPLORER_SHELL_Y);
589 int w = prefs.getInt(PrefsDialog.EXPLORER_SHELL_WIDTH);
590 int h = prefs.getInt(PrefsDialog.EXPLORER_SHELL_HEIGHT);
592 // check that we're not out of the display area
593 Rectangle rect = mDisplay.getClientArea();
594 // first check the width/height
595 if (w > rect.width) {
597 prefs.setValue(PrefsDialog.EXPLORER_SHELL_WIDTH, rect.width);
599 if (h > rect.height) {
601 prefs.setValue(PrefsDialog.EXPLORER_SHELL_HEIGHT, rect.height);
603 // then check x. Make sure the left corner is in the screen
606 prefs.setValue(PrefsDialog.EXPLORER_SHELL_X, rect.x);
607 } else if (x >= rect.x + rect.width) {
608 x = rect.x + rect.width - w;
609 prefs.setValue(PrefsDialog.EXPLORER_SHELL_X, rect.x);
611 // then check y. Make sure the left corner is in the screen
614 prefs.setValue(PrefsDialog.EXPLORER_SHELL_Y, rect.y);
615 } else if (y >= rect.y + rect.height) {
616 y = rect.y + rect.height - h;
617 prefs.setValue(PrefsDialog.EXPLORER_SHELL_Y, rect.y);
620 // now we can set the location/size
621 shell.setBounds(x, y, w, h);
623 // add listener for resize/move
624 shell.addControlListener(new ControlListener() {
625 public void controlMoved(ControlEvent e) {
627 Rectangle rect = shell.getBounds();
628 // store in pref file
629 PreferenceStore prefs = PrefsDialog.getStore();
630 prefs.setValue(PrefsDialog.EXPLORER_SHELL_X, rect.x);
631 prefs.setValue(PrefsDialog.EXPLORER_SHELL_Y, rect.y);
634 public void controlResized(ControlEvent e) {
636 Rectangle rect = shell.getBounds();
637 // store in pref file
638 PreferenceStore prefs = PrefsDialog.getStore();
639 prefs.setValue(PrefsDialog.EXPLORER_SHELL_WIDTH, rect.width);
640 prefs.setValue(PrefsDialog.EXPLORER_SHELL_HEIGHT, rect.height);
646 * Set the confirm-before-close dialog. TODO: enable/disable in prefs. TODO:
647 * is there any point in having this?
649 private void setConfirmClose(final Shell shell) {
653 shell.addListener(SWT.Close, new Listener() {
654 public void handleEvent(Event event) {
655 int style = SWT.APPLICATION_MODAL | SWT.YES | SWT.NO;
656 MessageBox msgBox = new MessageBox(shell, style);
657 msgBox.setText("Confirm...");
658 msgBox.setMessage("Close DDM?");
659 event.doit = (msgBox.open() == SWT.YES);
665 * Create the menu bar and items.
667 private void createMenus(final Shell shell) {
669 Menu menuBar = new Menu(shell, SWT.BAR);
671 // create top-level items
672 MenuItem fileItem = new MenuItem(menuBar, SWT.CASCADE);
673 fileItem.setText("&File");
674 MenuItem editItem = new MenuItem(menuBar, SWT.CASCADE);
675 editItem.setText("&Edit");
676 MenuItem actionItem = new MenuItem(menuBar, SWT.CASCADE);
677 actionItem.setText("&Actions");
678 MenuItem deviceItem = new MenuItem(menuBar, SWT.CASCADE);
679 deviceItem.setText("&Device");
680 MenuItem helpItem = new MenuItem(menuBar, SWT.CASCADE);
681 helpItem.setText("&Help");
683 // create top-level menus
684 Menu fileMenu = new Menu(menuBar);
685 fileItem.setMenu(fileMenu);
686 Menu editMenu = new Menu(menuBar);
687 editItem.setMenu(editMenu);
688 Menu actionMenu = new Menu(menuBar);
689 actionItem.setMenu(actionMenu);
690 Menu deviceMenu = new Menu(menuBar);
691 deviceItem.setMenu(deviceMenu);
692 Menu helpMenu = new Menu(menuBar);
693 helpItem.setMenu(helpMenu);
697 // create File menu items
698 item = new MenuItem(fileMenu, SWT.NONE);
699 item.setText("&Preferences...");
700 item.addSelectionListener(new SelectionAdapter() {
702 public void widgetSelected(SelectionEvent e) {
703 PrefsDialog.run(shell);
707 item = new MenuItem(fileMenu, SWT.NONE);
708 item.setText("&Static Port Configuration...");
709 item.addSelectionListener(new SelectionAdapter() {
711 public void widgetSelected(SelectionEvent e) {
712 StaticPortConfigDialog dlg = new StaticPortConfigDialog(shell);
717 new MenuItem(fileMenu, SWT.SEPARATOR);
719 item = new MenuItem(fileMenu, SWT.NONE);
720 item.setText("E&xit\tCtrl-Q");
721 item.setAccelerator('Q' | (Main.isMac() ? SWT.COMMAND : SWT.CONTROL));
722 item.addSelectionListener(new SelectionAdapter() {
724 public void widgetSelected(SelectionEvent e) {
729 // create edit menu items
730 mCopyMenuItem = new MenuItem(editMenu, SWT.NONE);
731 mCopyMenuItem.setText("&Copy\tCtrl-C");
732 mCopyMenuItem.setAccelerator('C' | SWT.COMMAND);
733 mCopyMenuItem.addSelectionListener(new SelectionAdapter() {
735 public void widgetSelected(SelectionEvent e) {
736 mTableListener.copy(mClipboard);
740 new MenuItem(editMenu, SWT.SEPARATOR);
742 mSelectAllMenuItem = new MenuItem(editMenu, SWT.NONE);
743 mSelectAllMenuItem.setText("Select &All\tCtrl-A");
744 mSelectAllMenuItem.setAccelerator('A' | SWT.COMMAND);
745 mSelectAllMenuItem.addSelectionListener(new SelectionAdapter() {
747 public void widgetSelected(SelectionEvent e) {
748 mTableListener.selectAll();
752 // create Action menu items
753 // TODO: this should come with a confirmation dialog
754 final MenuItem actionHaltItem = new MenuItem(actionMenu, SWT.NONE);
755 actionHaltItem.setText("&Halt VM");
756 actionHaltItem.addSelectionListener(new SelectionAdapter() {
758 public void widgetSelected(SelectionEvent e) {
759 mDevicePanel.killSelectedClient();
763 final MenuItem actionCauseGcItem = new MenuItem(actionMenu, SWT.NONE);
764 actionCauseGcItem.setText("Cause &GC");
765 actionCauseGcItem.addSelectionListener(new SelectionAdapter() {
767 public void widgetSelected(SelectionEvent e) {
768 mDevicePanel.forceGcOnSelectedClient();
772 final MenuItem actionResetAdb = new MenuItem(actionMenu, SWT.NONE);
773 actionResetAdb.setText("&Reset adb");
774 actionResetAdb.addSelectionListener(new SelectionAdapter() {
776 public void widgetSelected(SelectionEvent e) {
777 AndroidDebugBridge bridge = AndroidDebugBridge.getBridge();
778 if (bridge != null) {
784 // configure Action items based on current state
785 actionMenu.addMenuListener(new MenuAdapter() {
787 public void menuShown(MenuEvent e) {
788 actionHaltItem.setEnabled(mTBHalt.getEnabled() && mCurrentClient != null);
789 actionCauseGcItem.setEnabled(mTBCauseGc.getEnabled() && mCurrentClient != null);
790 actionResetAdb.setEnabled(true);
794 // create Device menu items
795 final MenuItem screenShotItem = new MenuItem(deviceMenu, SWT.NONE);
797 // The \tCtrl-S "keybinding text" here isn't right for the Mac - but
798 // it's stripped out and replaced by the proper keyboard accelerator
799 // text (e.g. the unicode symbol for the command key + S) anyway
800 // so it's fine to leave it there for the other platforms.
801 screenShotItem.setText("&Screen capture...\tCtrl-S");
802 screenShotItem.setAccelerator('S' | (Main.isMac() ? SWT.COMMAND : SWT.CONTROL));
803 screenShotItem.addSelectionListener(new SelectionAdapter() {
805 public void widgetSelected(SelectionEvent e) {
806 if (mCurrentDevice != null) {
807 ScreenShotDialog dlg = new ScreenShotDialog(shell);
808 dlg.open(mCurrentDevice);
813 new MenuItem(deviceMenu, SWT.SEPARATOR);
815 final MenuItem explorerItem = new MenuItem(deviceMenu, SWT.NONE);
816 explorerItem.setText("File Explorer...");
817 explorerItem.addSelectionListener(new SelectionAdapter() {
819 public void widgetSelected(SelectionEvent e) {
820 createFileExplorer();
824 new MenuItem(deviceMenu, SWT.SEPARATOR);
826 final MenuItem processItem = new MenuItem(deviceMenu, SWT.NONE);
827 processItem.setText("Show &process status...");
828 processItem.addSelectionListener(new SelectionAdapter() {
830 public void widgetSelected(SelectionEvent e) {
831 DeviceCommandDialog dlg;
832 dlg = new DeviceCommandDialog("ps -x", "ps-x.txt", shell);
833 dlg.open(mCurrentDevice);
837 final MenuItem deviceStateItem = new MenuItem(deviceMenu, SWT.NONE);
838 deviceStateItem.setText("Dump &device state...");
839 deviceStateItem.addSelectionListener(new SelectionAdapter() {
841 public void widgetSelected(SelectionEvent e) {
842 DeviceCommandDialog dlg;
843 dlg = new DeviceCommandDialog("/system/bin/dumpstate /proc/self/fd/0",
844 "device-state.txt", shell);
845 dlg.open(mCurrentDevice);
849 final MenuItem appStateItem = new MenuItem(deviceMenu, SWT.NONE);
850 appStateItem.setText("Dump &app state...");
851 appStateItem.setEnabled(false);
852 appStateItem.addSelectionListener(new SelectionAdapter() {
854 public void widgetSelected(SelectionEvent e) {
855 DeviceCommandDialog dlg;
856 dlg = new DeviceCommandDialog("dumpsys", "app-state.txt", shell);
857 dlg.open(mCurrentDevice);
861 final MenuItem radioStateItem = new MenuItem(deviceMenu, SWT.NONE);
862 radioStateItem.setText("Dump &radio state...");
863 radioStateItem.addSelectionListener(new SelectionAdapter() {
865 public void widgetSelected(SelectionEvent e) {
866 DeviceCommandDialog dlg;
867 dlg = new DeviceCommandDialog(
868 "cat /data/logs/radio.4 /data/logs/radio.3"
869 + " /data/logs/radio.2 /data/logs/radio.1"
870 + " /data/logs/radio",
871 "radio-state.txt", shell);
872 dlg.open(mCurrentDevice);
876 final MenuItem logCatItem = new MenuItem(deviceMenu, SWT.NONE);
877 logCatItem.setText("Run &logcat...");
878 logCatItem.addSelectionListener(new SelectionAdapter() {
880 public void widgetSelected(SelectionEvent e) {
881 DeviceCommandDialog dlg;
882 dlg = new DeviceCommandDialog("logcat '*:d jdwp:w'", "log.txt",
884 dlg.open(mCurrentDevice);
888 // configure Action items based on current state
889 deviceMenu.addMenuListener(new MenuAdapter() {
891 public void menuShown(MenuEvent e) {
892 boolean deviceEnabled = mCurrentDevice != null;
893 screenShotItem.setEnabled(deviceEnabled);
894 explorerItem.setEnabled(deviceEnabled);
895 processItem.setEnabled(deviceEnabled);
896 deviceStateItem.setEnabled(deviceEnabled);
897 appStateItem.setEnabled(deviceEnabled);
898 radioStateItem.setEnabled(deviceEnabled);
899 logCatItem.setEnabled(deviceEnabled);
903 // create Help menu items
904 item = new MenuItem(helpMenu, SWT.NONE);
905 item.setText("&Contents...");
906 item.addSelectionListener(new SelectionAdapter() {
908 public void widgetSelected(SelectionEvent e) {
909 int style = SWT.APPLICATION_MODAL | SWT.OK;
910 MessageBox msgBox = new MessageBox(shell, style);
911 msgBox.setText("Help!");
912 msgBox.setMessage("Help wanted.");
917 new MenuItem(helpMenu, SWT.SEPARATOR);
919 item = new MenuItem(helpMenu, SWT.NONE);
920 item.setText("&About...");
921 item.addSelectionListener(new SelectionAdapter() {
923 public void widgetSelected(SelectionEvent e) {
924 AboutDialog dlg = new AboutDialog(shell);
929 // tell the shell to use this menu
930 shell.setMenuBar(menuBar);
934 * Create the widgets in the main application window. The basic layout is a
935 * two-panel sash, with a scrolling list of VMs on the left and detailed
936 * output for a single VM on the right.
938 private void createWidgets(final Shell shell) {
939 Color darkGray = shell.getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY);
942 * Create three areas: tool bar, split panels, status line
944 shell.setLayout(new GridLayout(1, false));
947 final Composite panelArea = new Composite(shell, SWT.BORDER);
949 // make the panel area absorb all space
950 panelArea.setLayoutData(new GridData(GridData.FILL_BOTH));
953 mStatusLine = new Label(shell, SWT.NONE);
955 // make status line extend all the way across
956 mStatusLine.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
958 mStatusLine.setText("Initializing...");
961 * Configure the split-panel area.
963 final PreferenceStore prefs = PrefsDialog.getStore();
965 Composite topPanel = new Composite(panelArea, SWT.NONE);
966 final Sash sash = new Sash(panelArea, SWT.HORIZONTAL);
967 sash.setBackground(darkGray);
968 Composite bottomPanel = new Composite(panelArea, SWT.NONE);
970 panelArea.setLayout(new FormLayout());
972 createTopPanel(topPanel, darkGray);
973 createBottomPanel(bottomPanel);
976 FormData data = new FormData();
977 data.top = new FormAttachment(0, 0);
978 data.bottom = new FormAttachment(sash, 0);
979 data.left = new FormAttachment(0, 0);
980 data.right = new FormAttachment(100, 0);
981 topPanel.setLayoutData(data);
983 final FormData sashData = new FormData();
984 if (prefs != null && prefs.contains(PREFERENCE_LOGSASH)) {
985 sashData.top = new FormAttachment(0, prefs.getInt(
986 PREFERENCE_LOGSASH));
988 sashData.top = new FormAttachment(50,0); // 50% across
990 sashData.left = new FormAttachment(0, 0);
991 sashData.right = new FormAttachment(100, 0);
992 sash.setLayoutData(sashData);
994 data = new FormData();
995 data.top = new FormAttachment(sash, 0);
996 data.bottom = new FormAttachment(100, 0);
997 data.left = new FormAttachment(0, 0);
998 data.right = new FormAttachment(100, 0);
999 bottomPanel.setLayoutData(data);
1001 // allow resizes, but cap at minPanelWidth
1002 sash.addListener(SWT.Selection, new Listener() {
1003 public void handleEvent(Event e) {
1004 Rectangle sashRect = sash.getBounds();
1005 Rectangle panelRect = panelArea.getClientArea();
1006 int bottom = panelRect.height - sashRect.height - 100;
1007 e.y = Math.max(Math.min(e.y, bottom), 100);
1008 if (e.y != sashRect.y) {
1009 sashData.top = new FormAttachment(0, e.y);
1010 prefs.setValue(PREFERENCE_LOGSASH, e.y);
1016 // add a global focus listener for all the tables
1017 mTableListener = new TableFocusListener();
1019 // now set up the listener in the various panels
1020 mLogPanel.setTableFocusListener(mTableListener);
1021 mEventLogPanel.setTableFocusListener(mTableListener);
1022 for (TablePanel p : mPanels) {
1024 p.setTableFocusListener(mTableListener);
1028 mStatusLine.setText("");
1032 * Populate the tool bar.
1034 private void createDevicePanelToolBar(ToolBar toolBar) {
1035 Display display = toolBar.getDisplay();
1037 // add "show heap updates" button
1038 mTBShowHeapUpdates = new ToolItem(toolBar, SWT.CHECK);
1039 mTBShowHeapUpdates.setImage(mDdmUiLibLoader.loadImage(display,
1040 DevicePanel.ICON_HEAP, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1041 mTBShowHeapUpdates.setToolTipText("Show heap updates");
1042 mTBShowHeapUpdates.setEnabled(false);
1043 mTBShowHeapUpdates.addSelectionListener(new SelectionAdapter() {
1045 public void widgetSelected(SelectionEvent e) {
1046 if (mCurrentClient != null) {
1047 // boolean status = ((ToolItem)e.item).getSelection();
1048 // invert previous state
1049 boolean enable = !mCurrentClient.isHeapUpdateEnabled();
1050 mCurrentClient.setHeapUpdateEnabled(enable);
1052 e.doit = false; // this has no effect?
1057 // add "dump HPROF" button
1058 mTBDumpHprof = new ToolItem(toolBar, SWT.PUSH);
1059 mTBDumpHprof.setToolTipText("Dump HPROF file");
1060 mTBDumpHprof.setEnabled(false);
1061 mTBDumpHprof.setImage(mDdmUiLibLoader.loadImage(display,
1062 DevicePanel.ICON_HPROF, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1063 mTBDumpHprof.addSelectionListener(new SelectionAdapter() {
1065 public void widgetSelected(SelectionEvent e) {
1066 mDevicePanel.dumpHprof();
1068 // this will make sure the dump hprof button is disabled for the current selection
1069 // as the client is already dumping an hprof file
1074 // add "cause GC" button
1075 mTBCauseGc = new ToolItem(toolBar, SWT.PUSH);
1076 mTBCauseGc.setToolTipText("Cause an immediate GC");
1077 mTBCauseGc.setEnabled(false);
1078 mTBCauseGc.setImage(mDdmUiLibLoader.loadImage(display,
1079 DevicePanel.ICON_GC, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1080 mTBCauseGc.addSelectionListener(new SelectionAdapter() {
1082 public void widgetSelected(SelectionEvent e) {
1083 mDevicePanel.forceGcOnSelectedClient();
1087 new ToolItem(toolBar, SWT.SEPARATOR);
1089 // add "show thread updates" button
1090 mTBShowThreadUpdates = new ToolItem(toolBar, SWT.CHECK);
1091 mTBShowThreadUpdates.setImage(mDdmUiLibLoader.loadImage(display,
1092 DevicePanel.ICON_THREAD, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1093 mTBShowThreadUpdates.setToolTipText("Show thread updates");
1094 mTBShowThreadUpdates.setEnabled(false);
1095 mTBShowThreadUpdates.addSelectionListener(new SelectionAdapter() {
1097 public void widgetSelected(SelectionEvent e) {
1098 if (mCurrentClient != null) {
1099 // boolean status = ((ToolItem)e.item).getSelection();
1100 // invert previous state
1101 boolean enable = !mCurrentClient.isThreadUpdateEnabled();
1103 mCurrentClient.setThreadUpdateEnabled(enable);
1105 e.doit = false; // this has no effect?
1110 // add a start/stop method tracing
1111 mTracingStartImage = mDdmUiLibLoader.loadImage(display,
1112 DevicePanel.ICON_TRACING_START,
1113 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null);
1114 mTracingStopImage = mDdmUiLibLoader.loadImage(display,
1115 DevicePanel.ICON_TRACING_STOP,
1116 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null);
1117 mTBProfiling = new ToolItem(toolBar, SWT.PUSH);
1118 mTBProfiling.setToolTipText("Start Method Profiling");
1119 mTBProfiling.setEnabled(false);
1120 mTBProfiling.setImage(mTracingStartImage);
1121 mTBProfiling.addSelectionListener(new SelectionAdapter() {
1123 public void widgetSelected(SelectionEvent e) {
1124 mDevicePanel.toggleMethodProfiling();
1128 new ToolItem(toolBar, SWT.SEPARATOR);
1130 // add "kill VM" button; need to make this visually distinct from
1131 // the status update buttons
1132 mTBHalt = new ToolItem(toolBar, SWT.PUSH);
1133 mTBHalt.setToolTipText("Halt the target VM");
1134 mTBHalt.setEnabled(false);
1135 mTBHalt.setImage(mDdmUiLibLoader.loadImage(display,
1136 DevicePanel.ICON_HALT, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1137 mTBHalt.addSelectionListener(new SelectionAdapter() {
1139 public void widgetSelected(SelectionEvent e) {
1140 mDevicePanel.killSelectedClient();
1147 private void createTopPanel(final Composite comp, Color darkGray) {
1148 final PreferenceStore prefs = PrefsDialog.getStore();
1150 comp.setLayout(new FormLayout());
1152 Composite leftPanel = new Composite(comp, SWT.NONE);
1153 final Sash sash = new Sash(comp, SWT.VERTICAL);
1154 sash.setBackground(darkGray);
1155 Composite rightPanel = new Composite(comp, SWT.NONE);
1157 createLeftPanel(leftPanel);
1158 createRightPanel(rightPanel);
1160 FormData data = new FormData();
1161 data.top = new FormAttachment(0, 0);
1162 data.bottom = new FormAttachment(100, 0);
1163 data.left = new FormAttachment(0, 0);
1164 data.right = new FormAttachment(sash, 0);
1165 leftPanel.setLayoutData(data);
1167 final FormData sashData = new FormData();
1168 sashData.top = new FormAttachment(0, 0);
1169 sashData.bottom = new FormAttachment(100, 0);
1170 if (prefs != null && prefs.contains(PREFERENCE_SASH)) {
1171 sashData.left = new FormAttachment(0, prefs.getInt(
1174 // position the sash 380 from the right instead of x% (done by using
1175 // FormAttachment(x, 0)) in order to keep the sash at the same
1177 // from the left when the window is resized.
1178 // 380px is just enough to display the left table with no horizontal
1179 // scrollbar with the default font.
1180 sashData.left = new FormAttachment(0, 380);
1182 sash.setLayoutData(sashData);
1184 data = new FormData();
1185 data.top = new FormAttachment(0, 0);
1186 data.bottom = new FormAttachment(100, 0);
1187 data.left = new FormAttachment(sash, 0);
1188 data.right = new FormAttachment(100, 0);
1189 rightPanel.setLayoutData(data);
1191 final int minPanelWidth = 60;
1193 // allow resizes, but cap at minPanelWidth
1194 sash.addListener(SWT.Selection, new Listener() {
1195 public void handleEvent(Event e) {
1196 Rectangle sashRect = sash.getBounds();
1197 Rectangle panelRect = comp.getClientArea();
1198 int right = panelRect.width - sashRect.width - minPanelWidth;
1199 e.x = Math.max(Math.min(e.x, right), minPanelWidth);
1200 if (e.x != sashRect.x) {
1201 sashData.left = new FormAttachment(0, e.x);
1202 prefs.setValue(PREFERENCE_SASH, e.x);
1209 private void createBottomPanel(final Composite comp) {
1210 final PreferenceStore prefs = PrefsDialog.getStore();
1213 Display display = comp.getDisplay();
1214 mClipboard = new Clipboard(display);
1216 LogColors colors = new LogColors();
1218 colors.infoColor = new Color(display, 0, 127, 0);
1219 colors.debugColor = new Color(display, 0, 0, 127);
1220 colors.errorColor = new Color(display, 255, 0, 0);
1221 colors.warningColor = new Color(display, 255, 127, 0);
1222 colors.verboseColor = new Color(display, 0, 0, 0);
1224 // set the preferences names
1225 LogPanel.PREFS_TIME = PREFS_COL_TIME;
1226 LogPanel.PREFS_LEVEL = PREFS_COL_LEVEL;
1227 LogPanel.PREFS_PID = PREFS_COL_PID;
1228 LogPanel.PREFS_TAG = PREFS_COL_TAG;
1229 LogPanel.PREFS_MESSAGE = PREFS_COL_MESSAGE;
1231 comp.setLayout(new GridLayout(1, false));
1233 ToolBar toolBar = new ToolBar(comp, SWT.HORIZONTAL);
1235 mCreateFilterAction = new ToolItemAction(toolBar, SWT.PUSH);
1236 mCreateFilterAction.item.setToolTipText("Create Filter");
1237 mCreateFilterAction.item.setImage(mDdmUiLibLoader.loadImage(mDisplay,
1238 "add.png", //$NON-NLS-1$
1239 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1240 mCreateFilterAction.item.addSelectionListener(new SelectionAdapter() {
1242 public void widgetSelected(SelectionEvent e) {
1243 mLogPanel.addFilter();
1247 mEditFilterAction = new ToolItemAction(toolBar, SWT.PUSH);
1248 mEditFilterAction.item.setToolTipText("Edit Filter");
1249 mEditFilterAction.item.setImage(mDdmUiLibLoader.loadImage(mDisplay,
1250 "edit.png", //$NON-NLS-1$
1251 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1252 mEditFilterAction.item.addSelectionListener(new SelectionAdapter() {
1254 public void widgetSelected(SelectionEvent e) {
1255 mLogPanel.editFilter();
1259 mDeleteFilterAction = new ToolItemAction(toolBar, SWT.PUSH);
1260 mDeleteFilterAction.item.setToolTipText("Delete Filter");
1261 mDeleteFilterAction.item.setImage(mDdmUiLibLoader.loadImage(mDisplay,
1262 "delete.png", //$NON-NLS-1$
1263 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1264 mDeleteFilterAction.item.addSelectionListener(new SelectionAdapter() {
1266 public void widgetSelected(SelectionEvent e) {
1267 mLogPanel.deleteFilter();
1272 new ToolItem(toolBar, SWT.SEPARATOR);
1274 LogLevel[] levels = LogLevel.values();
1275 mLogLevelActions = new ToolItemAction[mLogLevelIcons.length];
1276 for (int i = 0 ; i < mLogLevelActions.length; i++) {
1277 String name = levels[i].getStringValue();
1278 final ToolItemAction newAction = new ToolItemAction(toolBar, SWT.CHECK);
1279 mLogLevelActions[i] = newAction;
1280 //newAction.item.setText(name);
1281 newAction.item.addSelectionListener(new SelectionAdapter() {
1283 public void widgetSelected(SelectionEvent e) {
1284 // disable the other actions and record current index
1285 for (int i = 0 ; i < mLogLevelActions.length; i++) {
1286 ToolItemAction a = mLogLevelActions[i];
1287 if (a == newAction) {
1290 // set the log level
1291 mLogPanel.setCurrentFilterLogLevel(i+2);
1293 a.setChecked(false);
1299 newAction.item.setToolTipText(name);
1300 newAction.item.setImage(mDdmUiLibLoader.loadImage(mDisplay,
1302 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1305 new ToolItem(toolBar, SWT.SEPARATOR);
1307 mClearAction = new ToolItemAction(toolBar, SWT.PUSH);
1308 mClearAction.item.setToolTipText("Clear Log");
1310 mClearAction.item.setImage(mDdmUiLibLoader.loadImage(mDisplay,
1311 "clear.png", //$NON-NLS-1$
1312 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1313 mClearAction.item.addSelectionListener(new SelectionAdapter() {
1315 public void widgetSelected(SelectionEvent e) {
1320 new ToolItem(toolBar, SWT.SEPARATOR);
1322 mExportAction = new ToolItemAction(toolBar, SWT.PUSH);
1323 mExportAction.item.setToolTipText("Export Selection As Text...");
1324 mExportAction.item.setImage(mDdmUiLibLoader.loadImage(mDisplay,
1325 "save.png", //$NON-NLS-1$
1326 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1327 mExportAction.item.addSelectionListener(new SelectionAdapter() {
1329 public void widgetSelected(SelectionEvent e) {
1337 // now create the log view
1338 mLogPanel = new LogPanel(colors, new FilterStorage(), LogPanel.FILTER_MANUAL);
1340 mLogPanel.setActions(mDeleteFilterAction, mEditFilterAction, mLogLevelActions);
1342 String colMode = prefs.getString(PrefsDialog.LOGCAT_COLUMN_MODE);
1343 if (PrefsDialog.LOGCAT_COLUMN_MODE_AUTO.equals(colMode)) {
1344 mLogPanel.setColumnMode(LogPanel.COLUMN_MODE_AUTO);
1347 String fontStr = PrefsDialog.getStore().getString(PrefsDialog.LOGCAT_FONT);
1348 if (fontStr != null) {
1350 FontData fdat = new FontData(fontStr);
1351 mLogPanel.setFont(new Font(display, fdat));
1352 } catch (IllegalArgumentException e) {
1353 // Looks like fontStr isn't a valid font representation.
1354 // We do nothing in this case, the logcat view will use the default font.
1355 } catch (SWTError e2) {
1356 // Looks like the Font() constructor failed.
1357 // We do nothing in this case, the logcat view will use the default font.
1361 mLogPanel.createPanel(comp);
1363 // and start the logcat
1364 mLogPanel.startLogCat(mCurrentDevice);
1368 * Create the contents of the left panel: a table of VMs.
1370 private void createLeftPanel(final Composite comp) {
1371 comp.setLayout(new GridLayout(1, false));
1372 ToolBar toolBar = new ToolBar(comp, SWT.HORIZONTAL | SWT.RIGHT | SWT.WRAP);
1373 toolBar.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
1374 createDevicePanelToolBar(toolBar);
1376 Composite c = new Composite(comp, SWT.NONE);
1377 c.setLayoutData(new GridData(GridData.FILL_BOTH));
1379 mDevicePanel = new DevicePanel(true /* showPorts */);
1380 mDevicePanel.createPanel(c);
1382 // add ourselves to the device panel selection listener
1383 mDevicePanel.addSelectionListener(this);
1387 * Create the contents of the right panel: tabs with VM information.
1389 private void createRightPanel(final Composite comp) {
1391 TabFolder tabFolder;
1393 comp.setLayout(new FillLayout());
1395 tabFolder = new TabFolder(comp, SWT.NONE);
1397 for (int i = 0; i < mPanels.length; i++) {
1398 if (mPanels[i] != null) {
1399 item = new TabItem(tabFolder, SWT.NONE);
1400 item.setText(mPanelNames[i]);
1401 item.setToolTipText(mPanelTips[i]);
1402 item.setControl(mPanels[i].createPanel(tabFolder));
1406 // add the emulator control panel to the folders.
1407 item = new TabItem(tabFolder, SWT.NONE);
1408 item.setText("Emulator Control");
1409 item.setToolTipText("Emulator Control Panel");
1410 mEmulatorPanel = new EmulatorControlPanel();
1411 item.setControl(mEmulatorPanel.createPanel(tabFolder));
1413 // add the event log panel to the folders.
1414 item = new TabItem(tabFolder, SWT.NONE);
1415 item.setText("Event Log");
1416 item.setToolTipText("Event Log");
1418 // create the composite that will hold the toolbar and the event log panel.
1419 Composite eventLogTopComposite = new Composite(tabFolder, SWT.NONE);
1420 item.setControl(eventLogTopComposite);
1421 eventLogTopComposite.setLayout(new GridLayout(1, false));
1423 // create the toolbar and the actions
1424 ToolBar toolbar = new ToolBar(eventLogTopComposite, SWT.HORIZONTAL);
1425 toolbar.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
1427 ToolItemAction optionsAction = new ToolItemAction(toolbar, SWT.PUSH);
1428 optionsAction.item.setToolTipText("Opens the options panel");
1429 optionsAction.item.setImage(mDdmUiLibLoader.loadImage(comp.getDisplay(),
1430 "edit.png", //$NON-NLS-1$
1431 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1433 ToolItemAction clearAction = new ToolItemAction(toolbar, SWT.PUSH);
1434 clearAction.item.setToolTipText("Clears the event log");
1435 clearAction.item.setImage(mDdmUiLibLoader.loadImage(comp.getDisplay(),
1436 "clear.png", //$NON-NLS-1$
1437 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1439 new ToolItem(toolbar, SWT.SEPARATOR);
1441 ToolItemAction saveAction = new ToolItemAction(toolbar, SWT.PUSH);
1442 saveAction.item.setToolTipText("Saves the event log");
1443 saveAction.item.setImage(mDdmUiLibLoader.loadImage(comp.getDisplay(),
1444 "save.png", //$NON-NLS-1$
1445 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1447 ToolItemAction loadAction = new ToolItemAction(toolbar, SWT.PUSH);
1448 loadAction.item.setToolTipText("Loads an event log");
1449 loadAction.item.setImage(mDdmUiLibLoader.loadImage(comp.getDisplay(),
1450 "load.png", //$NON-NLS-1$
1451 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1453 ToolItemAction importBugAction = new ToolItemAction(toolbar, SWT.PUSH);
1454 importBugAction.item.setToolTipText("Imports a bug report");
1455 importBugAction.item.setImage(mDdmUiLibLoader.loadImage(comp.getDisplay(),
1456 "importBug.png", //$NON-NLS-1$
1457 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1459 // create the event log panel
1460 mEventLogPanel = new EventLogPanel();
1462 // set the external actions
1463 mEventLogPanel.setActions(optionsAction, clearAction, saveAction, loadAction,
1467 mEventLogPanel.createPanel(eventLogTopComposite);
1470 private void createFileExplorer() {
1471 if (mExplorer == null) {
1472 mExplorerShell = new Shell(mDisplay);
1475 mExplorerShell.setLayout(new GridLayout(1, false));
1478 ToolBar toolBar = new ToolBar(mExplorerShell, SWT.HORIZONTAL);
1479 toolBar.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
1481 ToolItemAction pullAction = new ToolItemAction(toolBar, SWT.PUSH);
1482 pullAction.item.setToolTipText("Pull File from Device");
1483 Image image = mDdmUiLibLoader.loadImage("pull.png", mDisplay); //$NON-NLS-1$
1484 if (image != null) {
1485 pullAction.item.setImage(image);
1487 // this is for debugging purpose when the icon is missing
1488 pullAction.item.setText("Pull"); //$NON-NLS-1$
1491 ToolItemAction pushAction = new ToolItemAction(toolBar, SWT.PUSH);
1492 pushAction.item.setToolTipText("Push file onto Device");
1493 image = mDdmUiLibLoader.loadImage("push.png", mDisplay); //$NON-NLS-1$
1494 if (image != null) {
1495 pushAction.item.setImage(image);
1497 // this is for debugging purpose when the icon is missing
1498 pushAction.item.setText("Push"); //$NON-NLS-1$
1501 ToolItemAction deleteAction = new ToolItemAction(toolBar, SWT.PUSH);
1502 deleteAction.item.setToolTipText("Delete");
1503 image = mDdmUiLibLoader.loadImage("delete.png", mDisplay); //$NON-NLS-1$
1504 if (image != null) {
1505 deleteAction.item.setImage(image);
1507 // this is for debugging purpose when the icon is missing
1508 deleteAction.item.setText("Delete"); //$NON-NLS-1$
1512 mExplorer = new DeviceExplorer();
1513 mExplorer.setActions(pushAction, pullAction, deleteAction);
1515 pullAction.item.addSelectionListener(new SelectionAdapter() {
1517 public void widgetSelected(SelectionEvent e) {
1518 mExplorer.pullSelection();
1521 pullAction.setEnabled(false);
1523 pushAction.item.addSelectionListener(new SelectionAdapter() {
1525 public void widgetSelected(SelectionEvent e) {
1526 mExplorer.pushIntoSelection();
1529 pushAction.setEnabled(false);
1531 deleteAction.item.addSelectionListener(new SelectionAdapter() {
1533 public void widgetSelected(SelectionEvent e) {
1534 mExplorer.deleteSelection();
1537 deleteAction.setEnabled(false);
1539 Composite parent = new Composite(mExplorerShell, SWT.NONE);
1540 parent.setLayoutData(new GridData(GridData.FILL_BOTH));
1542 mExplorer.createPanel(parent);
1543 mExplorer.switchDevice(mCurrentDevice);
1545 mExplorerShell.addShellListener(new ShellListener() {
1546 public void shellActivated(ShellEvent e) {
1550 public void shellClosed(ShellEvent e) {
1552 mExplorerShell = null;
1555 public void shellDeactivated(ShellEvent e) {
1559 public void shellDeiconified(ShellEvent e) {
1563 public void shellIconified(ShellEvent e) {
1568 mExplorerShell.pack();
1569 setExplorerSizeAndPosition(mExplorerShell);
1570 mExplorerShell.open();
1572 if (mExplorerShell != null) {
1573 mExplorerShell.forceActive();
1579 * Set the status line. TODO: make this a stack, so we can safely have
1580 * multiple things trying to set it all at once. Also specify an expiration?
1582 public void setStatusLine(final String str) {
1584 mDisplay.asyncExec(new Runnable() {
1586 doSetStatusLine(str);
1589 } catch (SWTException swte) {
1590 if (!mDisplay.isDisposed())
1595 private void doSetStatusLine(String str) {
1596 if (mStatusLine.isDisposed())
1599 if (!mStatusLine.getText().equals(str)) {
1600 mStatusLine.setText(str);
1602 // try { Thread.sleep(100); }
1603 // catch (InterruptedException ie) {}
1607 public void displayError(final String msg) {
1609 mDisplay.syncExec(new Runnable() {
1611 MessageDialog.openError(mDisplay.getActiveShell(), "Error",
1615 } catch (SWTException swte) {
1616 if (!mDisplay.isDisposed())
1621 private void enableButtons() {
1622 if (mCurrentClient != null) {
1623 mTBShowThreadUpdates.setSelection(mCurrentClient.isThreadUpdateEnabled());
1624 mTBShowThreadUpdates.setEnabled(true);
1625 mTBShowHeapUpdates.setSelection(mCurrentClient.isHeapUpdateEnabled());
1626 mTBShowHeapUpdates.setEnabled(true);
1627 mTBHalt.setEnabled(true);
1628 mTBCauseGc.setEnabled(true);
1630 ClientData data = mCurrentClient.getClientData();
1632 if (data.hasFeature(ClientData.FEATURE_HPROF)) {
1633 mTBDumpHprof.setEnabled(data.hasPendingHprofDump() == false);
1634 mTBDumpHprof.setToolTipText("Dump HPROF file");
1636 mTBDumpHprof.setEnabled(false);
1637 mTBDumpHprof.setToolTipText("Dump HPROF file (not supported by this VM)");
1640 if (data.hasFeature(ClientData.FEATURE_PROFILING)) {
1641 mTBProfiling.setEnabled(true);
1642 if (data.getMethodProfilingStatus() == MethodProfilingStatus.ON) {
1643 mTBProfiling.setToolTipText("Stop Method Profiling");
1644 mTBProfiling.setImage(mTracingStopImage);
1646 mTBProfiling.setToolTipText("Start Method Profiling");
1647 mTBProfiling.setImage(mTracingStartImage);
1650 mTBProfiling.setEnabled(false);
1651 mTBProfiling.setImage(mTracingStartImage);
1652 mTBProfiling.setToolTipText("Start Method Profiling (not supported by this VM)");
1655 // list is empty, disable these
1656 mTBShowThreadUpdates.setSelection(false);
1657 mTBShowThreadUpdates.setEnabled(false);
1658 mTBShowHeapUpdates.setSelection(false);
1659 mTBShowHeapUpdates.setEnabled(false);
1660 mTBHalt.setEnabled(false);
1661 mTBCauseGc.setEnabled(false);
1663 mTBDumpHprof.setEnabled(false);
1664 mTBDumpHprof.setToolTipText("Dump HPROF file");
1666 mTBProfiling.setEnabled(false);
1667 mTBProfiling.setImage(mTracingStartImage);
1668 mTBProfiling.setToolTipText("Start Method Profiling");
1673 * Sent when a new {@link IDevice} and {@link Client} are selected.
1674 * @param selectedDevice the selected device. If null, no devices are selected.
1675 * @param selectedClient The selected client. If null, no clients are selected.
1677 * @see IUiSelectionListener
1679 public void selectionChanged(IDevice selectedDevice, Client selectedClient) {
1680 if (mCurrentDevice != selectedDevice) {
1681 mCurrentDevice = selectedDevice;
1682 for (TablePanel panel : mPanels) {
1683 if (panel != null) {
1684 panel.deviceSelected(mCurrentDevice);
1688 mEmulatorPanel.deviceSelected(mCurrentDevice);
1689 mLogPanel.deviceSelected(mCurrentDevice);
1690 if (mEventLogPanel != null) {
1691 mEventLogPanel.deviceSelected(mCurrentDevice);
1694 if (mExplorer != null) {
1695 mExplorer.switchDevice(mCurrentDevice);
1699 if (mCurrentClient != selectedClient) {
1700 AndroidDebugBridge.getBridge().setSelectedClient(selectedClient);
1701 mCurrentClient = selectedClient;
1702 for (TablePanel panel : mPanels) {
1703 if (panel != null) {
1704 panel.clientSelected(mCurrentClient);
1712 public void clientChanged(Client client, int changeMask) {
1713 if ((changeMask & Client.CHANGE_METHOD_PROFILING_STATUS) ==
1714 Client.CHANGE_METHOD_PROFILING_STATUS) {
1715 if (mCurrentClient == client) {
1716 mDisplay.asyncExec(new Runnable() {
1718 // force refresh of the button enabled state.