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.SyncService;
25 import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener;
26 import com.android.ddmlib.ClientData.IHprofDumpHandler;
27 import com.android.ddmlib.ClientData.MethodProfilingStatus;
28 import com.android.ddmlib.Log.ILogOutput;
29 import com.android.ddmlib.Log.LogLevel;
30 import com.android.ddmlib.SyncService.SyncResult;
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.ImageHelper;
37 import com.android.ddmuilib.ImageLoader;
38 import com.android.ddmuilib.InfoPanel;
39 import com.android.ddmuilib.NativeHeapPanel;
40 import com.android.ddmuilib.ScreenShotDialog;
41 import com.android.ddmuilib.SysinfoPanel;
42 import com.android.ddmuilib.TablePanel;
43 import com.android.ddmuilib.ThreadPanel;
44 import com.android.ddmuilib.DevicePanel.IUiSelectionListener;
45 import com.android.ddmuilib.actions.ToolItemAction;
46 import com.android.ddmuilib.explorer.DeviceExplorer;
47 import com.android.ddmuilib.handler.BaseFileHandler;
48 import com.android.ddmuilib.handler.MethodProfilingHandler;
49 import com.android.ddmuilib.log.event.EventLogPanel;
50 import com.android.ddmuilib.logcat.LogColors;
51 import com.android.ddmuilib.logcat.LogFilter;
52 import com.android.ddmuilib.logcat.LogPanel;
53 import com.android.ddmuilib.logcat.LogPanel.ILogFilterStorageManager;
55 import org.eclipse.jface.dialogs.MessageDialog;
56 import org.eclipse.jface.preference.IPreferenceStore;
57 import org.eclipse.jface.preference.PreferenceStore;
58 import org.eclipse.swt.SWT;
59 import org.eclipse.swt.SWTError;
60 import org.eclipse.swt.SWTException;
61 import org.eclipse.swt.dnd.Clipboard;
62 import org.eclipse.swt.events.ControlEvent;
63 import org.eclipse.swt.events.ControlListener;
64 import org.eclipse.swt.events.MenuAdapter;
65 import org.eclipse.swt.events.MenuEvent;
66 import org.eclipse.swt.events.SelectionAdapter;
67 import org.eclipse.swt.events.SelectionEvent;
68 import org.eclipse.swt.events.ShellEvent;
69 import org.eclipse.swt.events.ShellListener;
70 import org.eclipse.swt.graphics.Color;
71 import org.eclipse.swt.graphics.Font;
72 import org.eclipse.swt.graphics.FontData;
73 import org.eclipse.swt.graphics.Image;
74 import org.eclipse.swt.graphics.Rectangle;
75 import org.eclipse.swt.layout.FillLayout;
76 import org.eclipse.swt.layout.FormAttachment;
77 import org.eclipse.swt.layout.FormData;
78 import org.eclipse.swt.layout.FormLayout;
79 import org.eclipse.swt.layout.GridData;
80 import org.eclipse.swt.layout.GridLayout;
81 import org.eclipse.swt.widgets.Composite;
82 import org.eclipse.swt.widgets.Display;
83 import org.eclipse.swt.widgets.Event;
84 import org.eclipse.swt.widgets.Label;
85 import org.eclipse.swt.widgets.Listener;
86 import org.eclipse.swt.widgets.Menu;
87 import org.eclipse.swt.widgets.MenuItem;
88 import org.eclipse.swt.widgets.MessageBox;
89 import org.eclipse.swt.widgets.Sash;
90 import org.eclipse.swt.widgets.Shell;
91 import org.eclipse.swt.widgets.TabFolder;
92 import org.eclipse.swt.widgets.TabItem;
93 import org.eclipse.swt.widgets.ToolBar;
94 import org.eclipse.swt.widgets.ToolItem;
97 import java.util.ArrayList;
100 * This acts as the UI builder. This cannot be its own thread since this prevent using AWT in an
101 * SWT application. So this class mainly builds the ui, and manages communication between the panels
102 * when {@link IDevice} / {@link Client} selection changes.
104 public class UIThread implements IUiSelectionListener, IClientChangeListener {
106 * UI tab panel definitions. The constants here must match up with the array
107 * indices in mPanels. PANEL_CLIENT_LIST is a "virtual" panel representing
110 public static final int PANEL_CLIENT_LIST = -1;
112 public static final int PANEL_INFO = 0;
114 public static final int PANEL_THREAD = 1;
116 public static final int PANEL_HEAP = 2;
118 public static final int PANEL_NATIVE_HEAP = 3;
120 private static final int PANEL_ALLOCATIONS = 4;
122 private static final int PANEL_SYSINFO = 5;
124 private static final int PANEL_COUNT = 6;
126 /** Content is setup in the constructor */
127 private static TablePanel[] mPanels = new TablePanel[PANEL_COUNT];
129 private static final String[] mPanelNames = new String[] {
130 "Info", "Threads", "VM Heap", "Native Heap", "Allocation Tracker", "Sysinfo"
133 private static final String[] mPanelTips = new String[] {
134 "Client information", "Thread status", "VM heap status",
135 "Native heap status", "Allocation Tracker", "Sysinfo graphs"
138 private static final String PREFERENCE_LOGSASH =
139 "logSashLocation"; //$NON-NLS-1$
140 private static final String PREFERENCE_SASH =
141 "sashLocation"; //$NON-NLS-1$
143 private static final String PREFS_COL_TIME =
144 "logcat.time"; //$NON-NLS-1$
145 private static final String PREFS_COL_LEVEL =
146 "logcat.level"; //$NON-NLS-1$
147 private static final String PREFS_COL_PID =
148 "logcat.pid"; //$NON-NLS-1$
149 private static final String PREFS_COL_TAG =
150 "logcat.tag"; //$NON-NLS-1$
151 private static final String PREFS_COL_MESSAGE =
152 "logcat.message"; //$NON-NLS-1$
154 private static final String PREFS_FILTERS = "logcat.filter"; //$NON-NLS-1$
156 // singleton instance
157 private static UIThread mInstance = new UIThread();
160 private Display mDisplay;
162 // the table we show in the left-hand pane
163 private DevicePanel mDevicePanel;
165 private IDevice mCurrentDevice = null;
166 private Client mCurrentClient = null;
168 // status line at the bottom of the app window
169 private Label mStatusLine;
171 // some toolbar items we need to update
172 private ToolItem mTBShowThreadUpdates;
173 private ToolItem mTBShowHeapUpdates;
174 private ToolItem mTBHalt;
175 private ToolItem mTBCauseGc;
176 private ToolItem mTBDumpHprof;
177 private ToolItem mTBProfiling;
179 private ImageLoader mDdmsImageLoader;
180 private ImageLoader mDdmuiLibImageLoader;
182 private final class FilterStorage implements ILogFilterStorageManager {
184 public LogFilter[] getFilterFromStore() {
185 String filterPrefs = PrefsDialog.getStore().getString(
188 // split in a string per filter
189 String[] filters = filterPrefs.split("\\|"); //$NON-NLS-1$
191 ArrayList<LogFilter> list =
192 new ArrayList<LogFilter>(filters.length);
194 for (String f : filters) {
195 if (f.length() > 0) {
196 LogFilter logFilter = new LogFilter();
197 if (logFilter.loadFromString(f)) {
203 return list.toArray(new LogFilter[list.size()]);
206 public void saveFilters(LogFilter[] filters) {
207 StringBuilder sb = new StringBuilder();
208 for (LogFilter f : filters) {
209 String filterString = f.toString();
210 sb.append(filterString);
214 PrefsDialog.getStore().setValue(PREFS_FILTERS, sb.toString());
217 public boolean requiresDefaultFilter() {
223 private LogPanel mLogPanel;
225 private ToolItemAction mCreateFilterAction;
226 private ToolItemAction mDeleteFilterAction;
227 private ToolItemAction mEditFilterAction;
228 private ToolItemAction mExportAction;
229 private ToolItemAction mClearAction;
231 private ToolItemAction[] mLogLevelActions;
232 private String[] mLogLevelIcons = {
233 "v.png", //$NON-NLS-1S
234 "d.png", //$NON-NLS-1S
235 "i.png", //$NON-NLS-1S
236 "w.png", //$NON-NLS-1S
237 "e.png", //$NON-NLS-1S
240 protected Clipboard mClipboard;
242 private MenuItem mCopyMenuItem;
244 private MenuItem mSelectAllMenuItem;
246 private TableFocusListener mTableListener;
248 private DeviceExplorer mExplorer = null;
249 private Shell mExplorerShell = null;
251 private EmulatorControlPanel mEmulatorPanel;
253 private EventLogPanel mEventLogPanel;
255 private Image mTracingStartImage;
257 private Image mTracingStopImage;
259 private class TableFocusListener implements ITableFocusListener {
261 private IFocusedTableActivator mCurrentActivator;
263 public void focusGained(IFocusedTableActivator activator) {
264 mCurrentActivator = activator;
265 if (mCopyMenuItem.isDisposed() == false) {
266 mCopyMenuItem.setEnabled(true);
267 mSelectAllMenuItem.setEnabled(true);
271 public void focusLost(IFocusedTableActivator activator) {
272 // if we move from one table to another, it's unclear
273 // if the old table lose its focus before the new
274 // one gets the focus, so we need to check.
275 if (activator == mCurrentActivator) {
277 if (mCopyMenuItem.isDisposed() == false) {
278 mCopyMenuItem.setEnabled(false);
279 mSelectAllMenuItem.setEnabled(false);
284 public void copy(Clipboard clipboard) {
285 if (mCurrentActivator != null) {
286 mCurrentActivator.copy(clipboard);
290 public void selectAll() {
291 if (mCurrentActivator != null) {
292 mCurrentActivator.selectAll();
299 * Handler for HPROF dumps.
300 * This will always prompt the user to save the HPROF file.
302 private class HProfHandler extends BaseFileHandler implements IHprofDumpHandler {
304 public HProfHandler(Shell parentShell) {
308 public void onFailure(final Client client) {
309 mDisplay.asyncExec(new Runnable() {
312 displayError("Unable to create HPROF file for application '%1$s'.\n" +
313 "Check logcat for more information.",
314 client.getClientData().getClientDescription());
316 // this will make sure the dump hprof button is re-enabled for the
317 // current selection. as the client is finished dumping an hprof file
324 public void onSuccess(final String remoteFilePath, final Client client) {
325 mDisplay.asyncExec(new Runnable() {
327 final IDevice device = client.getDevice();
329 // get the sync service to pull the HPROF file
330 final SyncService sync = client.getDevice().getSyncService();
332 SyncResult result = promptAndPull(sync,
333 client.getClientData().getClientDescription() + ".hprof",
334 remoteFilePath, "Save HPROF file");
335 if (result != null && result.getCode() != SyncService.RESULT_OK) {
337 "Unable to download HPROF file from device '%1$s'.\n\n%2$s",
338 device.getSerialNumber(), result.getMessage());
341 displayError("Unable to download HPROF file from device '%1$s'.",
342 device.getSerialNumber());
344 } catch (Exception e) {
345 displayError("Unable to download HPROF file from device '%1$s'.",
346 device.getSerialNumber());
349 // this will make sure the dump hprof button is re-enabled for the
350 // current selection. as the client is finished dumping an hprof file
357 private void displayError(String format, Object... args) {
358 MessageDialog.openError(mParentShell, "HPROF Error",
359 String.format(format, args));
365 * Generic constructor.
368 mPanels[PANEL_INFO] = new InfoPanel();
369 mPanels[PANEL_THREAD] = new ThreadPanel();
370 mPanels[PANEL_HEAP] = new HeapPanel();
371 if (PrefsDialog.getStore().getBoolean(PrefsDialog.SHOW_NATIVE_HEAP)) {
372 mPanels[PANEL_NATIVE_HEAP] = new NativeHeapPanel();
374 mPanels[PANEL_NATIVE_HEAP] = null;
376 mPanels[PANEL_ALLOCATIONS] = new AllocationPanel();
377 mPanels[PANEL_SYSINFO] = new SysinfoPanel();
381 * Get singleton instance of the UI thread.
383 public static UIThread getInstance() {
388 * Return the Display. Don't try this unless you're in the UI thread.
390 public Display getDisplay() {
394 public void asyncExec(Runnable r) {
395 if (mDisplay != null && mDisplay.isDisposed() == false) {
396 mDisplay.asyncExec(r);
400 /** returns the IPreferenceStore */
401 public IPreferenceStore getStore() {
402 return PrefsDialog.getStore();
406 * Create SWT objects and drive the user interface event loop.
407 * @param location location of the folder that contains ddms.
409 public void runUI(String ddmsParentLocation) {
410 Display.setAppName("ddms");
411 mDisplay = new Display();
412 final Shell shell = new Shell(mDisplay);
414 // create the image loaders for DDMS and DDMUILIB
415 mDdmsImageLoader = new ImageLoader(this.getClass());
416 mDdmuiLibImageLoader = new ImageLoader(DevicePanel.class);
418 shell.setImage(ImageHelper.loadImage(mDdmsImageLoader, mDisplay,
419 "ddms-icon.png", //$NON-NLS-1$
422 Log.setLogOutput(new ILogOutput() {
423 public void printAndPromptLog(final LogLevel logLevel, final String tag,
424 final String message) {
425 Log.printLog(logLevel, tag, message);
426 // dialog box only run in UI thread..
427 mDisplay.asyncExec(new Runnable() {
429 Shell shell = mDisplay.getActiveShell();
430 if (logLevel == LogLevel.ERROR) {
431 MessageDialog.openError(shell, tag, message);
433 MessageDialog.openWarning(shell, tag, message);
439 public void printLog(LogLevel logLevel, String tag, String message) {
440 Log.printLog(logLevel, tag, message);
444 // set the handler for hprof dump
445 ClientData.setHprofDumpHandler(new HProfHandler(shell));
446 ClientData.setMethodProfilingHandler(new MethodProfilingHandler(shell));
448 // [try to] ensure ADB is running
450 if (ddmsParentLocation != null && ddmsParentLocation.length() != 0) {
451 adbLocation = ddmsParentLocation + File.separator + "adb"; //$NON-NLS-1$
453 adbLocation = "adb"; //$NON-NLS-1$
456 AndroidDebugBridge.init(true /* debugger support */);
457 AndroidDebugBridge.createBridge(adbLocation, true /* forceNewBridge */);
459 // we need to listen to client change to be notified of client status (profiling) change
460 AndroidDebugBridge.addClientChangeListener(this);
462 shell.setText("Dalvik Debug Monitor");
463 setConfirmClose(shell);
465 createWidgets(shell);
468 setSizeAndPosition(shell);
471 Log.d("ddms", "UI is up");
473 while (!shell.isDisposed()) {
474 if (!mDisplay.readAndDispatch())
477 mLogPanel.stopLogCat(true);
479 mDevicePanel.dispose();
480 for (TablePanel panel : mPanels) {
487 Log.d("ddms", "UI is down");
491 * Set the size and position of the main window from the preference, and
492 * setup listeners for control events (resize/move of the window)
494 private void setSizeAndPosition(final Shell shell) {
495 shell.setMinimumSize(400, 200);
497 // get the x/y and w/h from the prefs
498 PreferenceStore prefs = PrefsDialog.getStore();
499 int x = prefs.getInt(PrefsDialog.SHELL_X);
500 int y = prefs.getInt(PrefsDialog.SHELL_Y);
501 int w = prefs.getInt(PrefsDialog.SHELL_WIDTH);
502 int h = prefs.getInt(PrefsDialog.SHELL_HEIGHT);
504 // check that we're not out of the display area
505 Rectangle rect = mDisplay.getClientArea();
506 // first check the width/height
507 if (w > rect.width) {
509 prefs.setValue(PrefsDialog.SHELL_WIDTH, rect.width);
511 if (h > rect.height) {
513 prefs.setValue(PrefsDialog.SHELL_HEIGHT, rect.height);
515 // then check x. Make sure the left corner is in the screen
518 prefs.setValue(PrefsDialog.SHELL_X, rect.x);
519 } else if (x >= rect.x + rect.width) {
520 x = rect.x + rect.width - w;
521 prefs.setValue(PrefsDialog.SHELL_X, rect.x);
523 // then check y. Make sure the left corner is in the screen
526 prefs.setValue(PrefsDialog.SHELL_Y, rect.y);
527 } else if (y >= rect.y + rect.height) {
528 y = rect.y + rect.height - h;
529 prefs.setValue(PrefsDialog.SHELL_Y, rect.y);
532 // now we can set the location/size
533 shell.setBounds(x, y, w, h);
535 // add listener for resize/move
536 shell.addControlListener(new ControlListener() {
537 public void controlMoved(ControlEvent e) {
539 Rectangle rect = shell.getBounds();
540 // store in pref file
541 PreferenceStore prefs = PrefsDialog.getStore();
542 prefs.setValue(PrefsDialog.SHELL_X, rect.x);
543 prefs.setValue(PrefsDialog.SHELL_Y, rect.y);
546 public void controlResized(ControlEvent e) {
548 Rectangle rect = shell.getBounds();
549 // store in pref file
550 PreferenceStore prefs = PrefsDialog.getStore();
551 prefs.setValue(PrefsDialog.SHELL_WIDTH, rect.width);
552 prefs.setValue(PrefsDialog.SHELL_HEIGHT, rect.height);
558 * Set the size and position of the file explorer window from the
559 * preference, and setup listeners for control events (resize/move of
562 private void setExplorerSizeAndPosition(final Shell shell) {
563 shell.setMinimumSize(400, 200);
565 // get the x/y and w/h from the prefs
566 PreferenceStore prefs = PrefsDialog.getStore();
567 int x = prefs.getInt(PrefsDialog.EXPLORER_SHELL_X);
568 int y = prefs.getInt(PrefsDialog.EXPLORER_SHELL_Y);
569 int w = prefs.getInt(PrefsDialog.EXPLORER_SHELL_WIDTH);
570 int h = prefs.getInt(PrefsDialog.EXPLORER_SHELL_HEIGHT);
572 // check that we're not out of the display area
573 Rectangle rect = mDisplay.getClientArea();
574 // first check the width/height
575 if (w > rect.width) {
577 prefs.setValue(PrefsDialog.EXPLORER_SHELL_WIDTH, rect.width);
579 if (h > rect.height) {
581 prefs.setValue(PrefsDialog.EXPLORER_SHELL_HEIGHT, rect.height);
583 // then check x. Make sure the left corner is in the screen
586 prefs.setValue(PrefsDialog.EXPLORER_SHELL_X, rect.x);
587 } else if (x >= rect.x + rect.width) {
588 x = rect.x + rect.width - w;
589 prefs.setValue(PrefsDialog.EXPLORER_SHELL_X, rect.x);
591 // then check y. Make sure the left corner is in the screen
594 prefs.setValue(PrefsDialog.EXPLORER_SHELL_Y, rect.y);
595 } else if (y >= rect.y + rect.height) {
596 y = rect.y + rect.height - h;
597 prefs.setValue(PrefsDialog.EXPLORER_SHELL_Y, rect.y);
600 // now we can set the location/size
601 shell.setBounds(x, y, w, h);
603 // add listener for resize/move
604 shell.addControlListener(new ControlListener() {
605 public void controlMoved(ControlEvent e) {
607 Rectangle rect = shell.getBounds();
608 // store in pref file
609 PreferenceStore prefs = PrefsDialog.getStore();
610 prefs.setValue(PrefsDialog.EXPLORER_SHELL_X, rect.x);
611 prefs.setValue(PrefsDialog.EXPLORER_SHELL_Y, rect.y);
614 public void controlResized(ControlEvent e) {
616 Rectangle rect = shell.getBounds();
617 // store in pref file
618 PreferenceStore prefs = PrefsDialog.getStore();
619 prefs.setValue(PrefsDialog.EXPLORER_SHELL_WIDTH, rect.width);
620 prefs.setValue(PrefsDialog.EXPLORER_SHELL_HEIGHT, rect.height);
626 * Set the confirm-before-close dialog. TODO: enable/disable in prefs. TODO:
627 * is there any point in having this?
629 private void setConfirmClose(final Shell shell) {
633 shell.addListener(SWT.Close, new Listener() {
634 public void handleEvent(Event event) {
635 int style = SWT.APPLICATION_MODAL | SWT.YES | SWT.NO;
636 MessageBox msgBox = new MessageBox(shell, style);
637 msgBox.setText("Confirm...");
638 msgBox.setMessage("Close DDM?");
639 event.doit = (msgBox.open() == SWT.YES);
645 * Create the menu bar and items.
647 private void createMenus(final Shell shell) {
649 Menu menuBar = new Menu(shell, SWT.BAR);
651 // create top-level items
652 MenuItem fileItem = new MenuItem(menuBar, SWT.CASCADE);
653 fileItem.setText("&File");
654 MenuItem editItem = new MenuItem(menuBar, SWT.CASCADE);
655 editItem.setText("&Edit");
656 MenuItem actionItem = new MenuItem(menuBar, SWT.CASCADE);
657 actionItem.setText("&Actions");
658 MenuItem deviceItem = new MenuItem(menuBar, SWT.CASCADE);
659 deviceItem.setText("&Device");
660 MenuItem helpItem = new MenuItem(menuBar, SWT.CASCADE);
661 helpItem.setText("&Help");
663 // create top-level menus
664 Menu fileMenu = new Menu(menuBar);
665 fileItem.setMenu(fileMenu);
666 Menu editMenu = new Menu(menuBar);
667 editItem.setMenu(editMenu);
668 Menu actionMenu = new Menu(menuBar);
669 actionItem.setMenu(actionMenu);
670 Menu deviceMenu = new Menu(menuBar);
671 deviceItem.setMenu(deviceMenu);
672 Menu helpMenu = new Menu(menuBar);
673 helpItem.setMenu(helpMenu);
677 // create File menu items
678 item = new MenuItem(fileMenu, SWT.NONE);
679 item.setText("&Preferences...");
680 item.addSelectionListener(new SelectionAdapter() {
682 public void widgetSelected(SelectionEvent e) {
683 PrefsDialog.run(shell);
687 item = new MenuItem(fileMenu, SWT.NONE);
688 item.setText("&Static Port Configuration...");
689 item.addSelectionListener(new SelectionAdapter() {
691 public void widgetSelected(SelectionEvent e) {
692 StaticPortConfigDialog dlg = new StaticPortConfigDialog(shell);
697 new MenuItem(fileMenu, SWT.SEPARATOR);
699 item = new MenuItem(fileMenu, SWT.NONE);
700 item.setText("E&xit\tCtrl-Q");
701 item.setAccelerator('Q' | SWT.CONTROL);
702 item.addSelectionListener(new SelectionAdapter() {
704 public void widgetSelected(SelectionEvent e) {
709 // create edit menu items
710 mCopyMenuItem = new MenuItem(editMenu, SWT.NONE);
711 mCopyMenuItem.setText("&Copy\tCtrl-C");
712 mCopyMenuItem.setAccelerator('C' | SWT.COMMAND);
713 mCopyMenuItem.addSelectionListener(new SelectionAdapter() {
715 public void widgetSelected(SelectionEvent e) {
716 mTableListener.copy(mClipboard);
720 new MenuItem(editMenu, SWT.SEPARATOR);
722 mSelectAllMenuItem = new MenuItem(editMenu, SWT.NONE);
723 mSelectAllMenuItem.setText("Select &All\tCtrl-A");
724 mSelectAllMenuItem.setAccelerator('A' | SWT.COMMAND);
725 mSelectAllMenuItem.addSelectionListener(new SelectionAdapter() {
727 public void widgetSelected(SelectionEvent e) {
728 mTableListener.selectAll();
732 // create Action menu items
733 // TODO: this should come with a confirmation dialog
734 final MenuItem actionHaltItem = new MenuItem(actionMenu, SWT.NONE);
735 actionHaltItem.setText("&Halt VM");
736 actionHaltItem.addSelectionListener(new SelectionAdapter() {
738 public void widgetSelected(SelectionEvent e) {
739 mDevicePanel.killSelectedClient();
743 final MenuItem actionCauseGcItem = new MenuItem(actionMenu, SWT.NONE);
744 actionCauseGcItem.setText("Cause &GC");
745 actionCauseGcItem.addSelectionListener(new SelectionAdapter() {
747 public void widgetSelected(SelectionEvent e) {
748 mDevicePanel.forceGcOnSelectedClient();
752 // configure Action items based on current state
753 actionMenu.addMenuListener(new MenuAdapter() {
755 public void menuShown(MenuEvent e) {
756 actionHaltItem.setEnabled(mTBHalt.getEnabled() && mCurrentClient != null);
757 actionCauseGcItem.setEnabled(mTBCauseGc.getEnabled() && mCurrentClient != null);
761 // create Device menu items
762 final MenuItem screenShotItem = new MenuItem(deviceMenu, SWT.NONE);
763 screenShotItem.setText("&Screen capture...\tCTrl-S");
764 screenShotItem.setAccelerator('S' | SWT.CONTROL);
765 screenShotItem.addSelectionListener(new SelectionAdapter() {
767 public void widgetSelected(SelectionEvent e) {
768 if (mCurrentDevice != null) {
769 ScreenShotDialog dlg = new ScreenShotDialog(shell);
770 dlg.open(mCurrentDevice);
775 new MenuItem(deviceMenu, SWT.SEPARATOR);
777 final MenuItem explorerItem = new MenuItem(deviceMenu, SWT.NONE);
778 explorerItem.setText("File Explorer...");
779 explorerItem.addSelectionListener(new SelectionAdapter() {
781 public void widgetSelected(SelectionEvent e) {
782 createFileExplorer();
786 new MenuItem(deviceMenu, SWT.SEPARATOR);
788 final MenuItem processItem = new MenuItem(deviceMenu, SWT.NONE);
789 processItem.setText("Show &process status...");
790 processItem.addSelectionListener(new SelectionAdapter() {
792 public void widgetSelected(SelectionEvent e) {
793 DeviceCommandDialog dlg;
794 dlg = new DeviceCommandDialog("ps -x", "ps-x.txt", shell);
795 dlg.open(mCurrentDevice);
799 final MenuItem deviceStateItem = new MenuItem(deviceMenu, SWT.NONE);
800 deviceStateItem.setText("Dump &device state...");
801 deviceStateItem.addSelectionListener(new SelectionAdapter() {
803 public void widgetSelected(SelectionEvent e) {
804 DeviceCommandDialog dlg;
805 dlg = new DeviceCommandDialog("/system/bin/dumpstate /proc/self/fd/0",
806 "device-state.txt", shell);
807 dlg.open(mCurrentDevice);
811 final MenuItem appStateItem = new MenuItem(deviceMenu, SWT.NONE);
812 appStateItem.setText("Dump &app state...");
813 appStateItem.setEnabled(false);
814 appStateItem.addSelectionListener(new SelectionAdapter() {
816 public void widgetSelected(SelectionEvent e) {
817 DeviceCommandDialog dlg;
818 dlg = new DeviceCommandDialog("dumpsys", "app-state.txt", shell);
819 dlg.open(mCurrentDevice);
823 final MenuItem radioStateItem = new MenuItem(deviceMenu, SWT.NONE);
824 radioStateItem.setText("Dump &radio state...");
825 radioStateItem.addSelectionListener(new SelectionAdapter() {
827 public void widgetSelected(SelectionEvent e) {
828 DeviceCommandDialog dlg;
829 dlg = new DeviceCommandDialog(
830 "cat /data/logs/radio.4 /data/logs/radio.3"
831 + " /data/logs/radio.2 /data/logs/radio.1"
832 + " /data/logs/radio",
833 "radio-state.txt", shell);
834 dlg.open(mCurrentDevice);
838 final MenuItem logCatItem = new MenuItem(deviceMenu, SWT.NONE);
839 logCatItem.setText("Run &logcat...");
840 logCatItem.addSelectionListener(new SelectionAdapter() {
842 public void widgetSelected(SelectionEvent e) {
843 DeviceCommandDialog dlg;
844 dlg = new DeviceCommandDialog("logcat '*:d jdwp:w'", "log.txt",
846 dlg.open(mCurrentDevice);
850 // configure Action items based on current state
851 deviceMenu.addMenuListener(new MenuAdapter() {
853 public void menuShown(MenuEvent e) {
854 boolean deviceEnabled = mCurrentDevice != null;
855 screenShotItem.setEnabled(deviceEnabled);
856 explorerItem.setEnabled(deviceEnabled);
857 processItem.setEnabled(deviceEnabled);
858 deviceStateItem.setEnabled(deviceEnabled);
859 appStateItem.setEnabled(deviceEnabled);
860 radioStateItem.setEnabled(deviceEnabled);
861 logCatItem.setEnabled(deviceEnabled);
865 // create Help menu items
866 item = new MenuItem(helpMenu, SWT.NONE);
867 item.setText("&Contents...");
868 item.addSelectionListener(new SelectionAdapter() {
870 public void widgetSelected(SelectionEvent e) {
871 int style = SWT.APPLICATION_MODAL | SWT.OK;
872 MessageBox msgBox = new MessageBox(shell, style);
873 msgBox.setText("Help!");
874 msgBox.setMessage("Help wanted.");
879 new MenuItem(helpMenu, SWT.SEPARATOR);
881 item = new MenuItem(helpMenu, SWT.NONE);
882 item.setText("&About...");
883 item.addSelectionListener(new SelectionAdapter() {
885 public void widgetSelected(SelectionEvent e) {
886 AboutDialog dlg = new AboutDialog(shell);
891 // tell the shell to use this menu
892 shell.setMenuBar(menuBar);
896 * Create the widgets in the main application window. The basic layout is a
897 * two-panel sash, with a scrolling list of VMs on the left and detailed
898 * output for a single VM on the right.
900 private void createWidgets(final Shell shell) {
901 Color darkGray = shell.getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY);
904 * Create three areas: tool bar, split panels, status line
906 shell.setLayout(new GridLayout(1, false));
909 final Composite panelArea = new Composite(shell, SWT.BORDER);
911 // make the panel area absorb all space
912 panelArea.setLayoutData(new GridData(GridData.FILL_BOTH));
915 mStatusLine = new Label(shell, SWT.NONE);
917 // make status line extend all the way across
918 mStatusLine.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
920 mStatusLine.setText("Initializing...");
923 * Configure the split-panel area.
925 final PreferenceStore prefs = PrefsDialog.getStore();
927 Composite topPanel = new Composite(panelArea, SWT.NONE);
928 final Sash sash = new Sash(panelArea, SWT.HORIZONTAL);
929 sash.setBackground(darkGray);
930 Composite bottomPanel = new Composite(panelArea, SWT.NONE);
932 panelArea.setLayout(new FormLayout());
934 createTopPanel(topPanel, darkGray);
935 createBottomPanel(bottomPanel);
938 FormData data = new FormData();
939 data.top = new FormAttachment(0, 0);
940 data.bottom = new FormAttachment(sash, 0);
941 data.left = new FormAttachment(0, 0);
942 data.right = new FormAttachment(100, 0);
943 topPanel.setLayoutData(data);
945 final FormData sashData = new FormData();
946 if (prefs != null && prefs.contains(PREFERENCE_LOGSASH)) {
947 sashData.top = new FormAttachment(0, prefs.getInt(
948 PREFERENCE_LOGSASH));
950 sashData.top = new FormAttachment(50,0); // 50% across
952 sashData.left = new FormAttachment(0, 0);
953 sashData.right = new FormAttachment(100, 0);
954 sash.setLayoutData(sashData);
956 data = new FormData();
957 data.top = new FormAttachment(sash, 0);
958 data.bottom = new FormAttachment(100, 0);
959 data.left = new FormAttachment(0, 0);
960 data.right = new FormAttachment(100, 0);
961 bottomPanel.setLayoutData(data);
963 // allow resizes, but cap at minPanelWidth
964 sash.addListener(SWT.Selection, new Listener() {
965 public void handleEvent(Event e) {
966 Rectangle sashRect = sash.getBounds();
967 Rectangle panelRect = panelArea.getClientArea();
968 int bottom = panelRect.height - sashRect.height - 100;
969 e.y = Math.max(Math.min(e.y, bottom), 100);
970 if (e.y != sashRect.y) {
971 sashData.top = new FormAttachment(0, e.y);
972 prefs.setValue(PREFERENCE_LOGSASH, e.y);
978 // add a global focus listener for all the tables
979 mTableListener = new TableFocusListener();
981 // now set up the listener in the various panels
982 mLogPanel.setTableFocusListener(mTableListener);
983 mEventLogPanel.setTableFocusListener(mTableListener);
984 for (TablePanel p : mPanels) {
986 p.setTableFocusListener(mTableListener);
990 mStatusLine.setText("");
994 * Populate the tool bar.
996 private void createDevicePanelToolBar(ToolBar toolBar) {
997 Display display = toolBar.getDisplay();
999 // add "show heap updates" button
1000 mTBShowHeapUpdates = new ToolItem(toolBar, SWT.CHECK);
1001 mTBShowHeapUpdates.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, display,
1002 DevicePanel.ICON_HEAP, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1003 mTBShowHeapUpdates.setToolTipText("Show heap updates");
1004 mTBShowHeapUpdates.setEnabled(false);
1005 mTBShowHeapUpdates.addSelectionListener(new SelectionAdapter() {
1007 public void widgetSelected(SelectionEvent e) {
1008 if (mCurrentClient != null) {
1009 // boolean status = ((ToolItem)e.item).getSelection();
1010 // invert previous state
1011 boolean enable = !mCurrentClient.isHeapUpdateEnabled();
1012 mCurrentClient.setHeapUpdateEnabled(enable);
1014 e.doit = false; // this has no effect?
1019 // add "dump HPROF" button
1020 mTBDumpHprof = new ToolItem(toolBar, SWT.PUSH);
1021 mTBDumpHprof.setToolTipText("Dump HPROF file");
1022 mTBDumpHprof.setEnabled(false);
1023 mTBDumpHprof.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, display,
1024 DevicePanel.ICON_HPROF, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1025 mTBDumpHprof.addSelectionListener(new SelectionAdapter() {
1027 public void widgetSelected(SelectionEvent e) {
1028 mDevicePanel.dumpHprof();
1030 // this will make sure the dump hprof button is disabled for the current selection
1031 // as the client is already dumping an hprof file
1036 // add "cause GC" button
1037 mTBCauseGc = new ToolItem(toolBar, SWT.PUSH);
1038 mTBCauseGc.setToolTipText("Cause an immediate GC");
1039 mTBCauseGc.setEnabled(false);
1040 mTBCauseGc.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, display,
1041 DevicePanel.ICON_GC, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1042 mTBCauseGc.addSelectionListener(new SelectionAdapter() {
1044 public void widgetSelected(SelectionEvent e) {
1045 mDevicePanel.forceGcOnSelectedClient();
1049 new ToolItem(toolBar, SWT.SEPARATOR);
1051 // add "show thread updates" button
1052 mTBShowThreadUpdates = new ToolItem(toolBar, SWT.CHECK);
1053 mTBShowThreadUpdates.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, display,
1054 DevicePanel.ICON_THREAD, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1055 mTBShowThreadUpdates.setToolTipText("Show thread updates");
1056 mTBShowThreadUpdates.setEnabled(false);
1057 mTBShowThreadUpdates.addSelectionListener(new SelectionAdapter() {
1059 public void widgetSelected(SelectionEvent e) {
1060 if (mCurrentClient != null) {
1061 // boolean status = ((ToolItem)e.item).getSelection();
1062 // invert previous state
1063 boolean enable = !mCurrentClient.isThreadUpdateEnabled();
1065 mCurrentClient.setThreadUpdateEnabled(enable);
1067 e.doit = false; // this has no effect?
1072 // add a start/stop method tracing
1073 mTracingStartImage = ImageHelper.loadImage(mDdmuiLibImageLoader, display,
1074 DevicePanel.ICON_TRACING_START,
1075 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null);
1076 mTracingStopImage = ImageHelper.loadImage(mDdmuiLibImageLoader, display,
1077 DevicePanel.ICON_TRACING_STOP,
1078 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null);
1079 mTBProfiling = new ToolItem(toolBar, SWT.PUSH);
1080 mTBProfiling.setToolTipText("Start Method Profiling");
1081 mTBProfiling.setEnabled(false);
1082 mTBProfiling.setImage(mTracingStartImage);
1083 mTBProfiling.addSelectionListener(new SelectionAdapter() {
1085 public void widgetSelected(SelectionEvent e) {
1086 mDevicePanel.toggleMethodProfiling();
1090 new ToolItem(toolBar, SWT.SEPARATOR);
1092 // add "kill VM" button; need to make this visually distinct from
1093 // the status update buttons
1094 mTBHalt = new ToolItem(toolBar, SWT.PUSH);
1095 mTBHalt.setToolTipText("Halt the target VM");
1096 mTBHalt.setEnabled(false);
1097 mTBHalt.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, display,
1098 DevicePanel.ICON_HALT, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1099 mTBHalt.addSelectionListener(new SelectionAdapter() {
1101 public void widgetSelected(SelectionEvent e) {
1102 mDevicePanel.killSelectedClient();
1109 private void createTopPanel(final Composite comp, Color darkGray) {
1110 final PreferenceStore prefs = PrefsDialog.getStore();
1112 comp.setLayout(new FormLayout());
1114 Composite leftPanel = new Composite(comp, SWT.NONE);
1115 final Sash sash = new Sash(comp, SWT.VERTICAL);
1116 sash.setBackground(darkGray);
1117 Composite rightPanel = new Composite(comp, SWT.NONE);
1119 createLeftPanel(leftPanel);
1120 createRightPanel(rightPanel);
1122 FormData data = new FormData();
1123 data.top = new FormAttachment(0, 0);
1124 data.bottom = new FormAttachment(100, 0);
1125 data.left = new FormAttachment(0, 0);
1126 data.right = new FormAttachment(sash, 0);
1127 leftPanel.setLayoutData(data);
1129 final FormData sashData = new FormData();
1130 sashData.top = new FormAttachment(0, 0);
1131 sashData.bottom = new FormAttachment(100, 0);
1132 if (prefs != null && prefs.contains(PREFERENCE_SASH)) {
1133 sashData.left = new FormAttachment(0, prefs.getInt(
1136 // position the sash 380 from the right instead of x% (done by using
1137 // FormAttachment(x, 0)) in order to keep the sash at the same
1139 // from the left when the window is resized.
1140 // 380px is just enough to display the left table with no horizontal
1141 // scrollbar with the default font.
1142 sashData.left = new FormAttachment(0, 380);
1144 sash.setLayoutData(sashData);
1146 data = new FormData();
1147 data.top = new FormAttachment(0, 0);
1148 data.bottom = new FormAttachment(100, 0);
1149 data.left = new FormAttachment(sash, 0);
1150 data.right = new FormAttachment(100, 0);
1151 rightPanel.setLayoutData(data);
1153 final int minPanelWidth = 60;
1155 // allow resizes, but cap at minPanelWidth
1156 sash.addListener(SWT.Selection, new Listener() {
1157 public void handleEvent(Event e) {
1158 Rectangle sashRect = sash.getBounds();
1159 Rectangle panelRect = comp.getClientArea();
1160 int right = panelRect.width - sashRect.width - minPanelWidth;
1161 e.x = Math.max(Math.min(e.x, right), minPanelWidth);
1162 if (e.x != sashRect.x) {
1163 sashData.left = new FormAttachment(0, e.x);
1164 prefs.setValue(PREFERENCE_SASH, e.x);
1171 private void createBottomPanel(final Composite comp) {
1172 final PreferenceStore prefs = PrefsDialog.getStore();
1175 Display display = comp.getDisplay();
1176 mClipboard = new Clipboard(display);
1178 LogColors colors = new LogColors();
1180 colors.infoColor = new Color(display, 0, 127, 0);
1181 colors.debugColor = new Color(display, 0, 0, 127);
1182 colors.errorColor = new Color(display, 255, 0, 0);
1183 colors.warningColor = new Color(display, 255, 127, 0);
1184 colors.verboseColor = new Color(display, 0, 0, 0);
1186 // set the preferences names
1187 LogPanel.PREFS_TIME = PREFS_COL_TIME;
1188 LogPanel.PREFS_LEVEL = PREFS_COL_LEVEL;
1189 LogPanel.PREFS_PID = PREFS_COL_PID;
1190 LogPanel.PREFS_TAG = PREFS_COL_TAG;
1191 LogPanel.PREFS_MESSAGE = PREFS_COL_MESSAGE;
1193 comp.setLayout(new GridLayout(1, false));
1195 ToolBar toolBar = new ToolBar(comp, SWT.HORIZONTAL);
1197 mCreateFilterAction = new ToolItemAction(toolBar, SWT.PUSH);
1198 mCreateFilterAction.item.setToolTipText("Create Filter");
1199 mCreateFilterAction.item.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, mDisplay,
1200 "add.png", //$NON-NLS-1$
1201 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1202 mCreateFilterAction.item.addSelectionListener(new SelectionAdapter() {
1204 public void widgetSelected(SelectionEvent e) {
1205 mLogPanel.addFilter();
1209 mEditFilterAction = new ToolItemAction(toolBar, SWT.PUSH);
1210 mEditFilterAction.item.setToolTipText("Edit Filter");
1211 mEditFilterAction.item.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, mDisplay,
1212 "edit.png", //$NON-NLS-1$
1213 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1214 mEditFilterAction.item.addSelectionListener(new SelectionAdapter() {
1216 public void widgetSelected(SelectionEvent e) {
1217 mLogPanel.editFilter();
1221 mDeleteFilterAction = new ToolItemAction(toolBar, SWT.PUSH);
1222 mDeleteFilterAction.item.setToolTipText("Delete Filter");
1223 mDeleteFilterAction.item.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, mDisplay,
1224 "delete.png", //$NON-NLS-1$
1225 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1226 mDeleteFilterAction.item.addSelectionListener(new SelectionAdapter() {
1228 public void widgetSelected(SelectionEvent e) {
1229 mLogPanel.deleteFilter();
1234 new ToolItem(toolBar, SWT.SEPARATOR);
1236 LogLevel[] levels = LogLevel.values();
1237 mLogLevelActions = new ToolItemAction[mLogLevelIcons.length];
1238 for (int i = 0 ; i < mLogLevelActions.length; i++) {
1239 String name = levels[i].getStringValue();
1240 final ToolItemAction newAction = new ToolItemAction(toolBar, SWT.CHECK);
1241 mLogLevelActions[i] = newAction;
1242 //newAction.item.setText(name);
1243 newAction.item.addSelectionListener(new SelectionAdapter() {
1245 public void widgetSelected(SelectionEvent e) {
1246 // disable the other actions and record current index
1247 for (int i = 0 ; i < mLogLevelActions.length; i++) {
1248 ToolItemAction a = mLogLevelActions[i];
1249 if (a == newAction) {
1252 // set the log level
1253 mLogPanel.setCurrentFilterLogLevel(i+2);
1255 a.setChecked(false);
1261 newAction.item.setToolTipText(name);
1262 newAction.item.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, mDisplay,
1264 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1267 new ToolItem(toolBar, SWT.SEPARATOR);
1269 mClearAction = new ToolItemAction(toolBar, SWT.PUSH);
1270 mClearAction.item.setToolTipText("Clear Log");
1272 mClearAction.item.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, mDisplay,
1273 "clear.png", //$NON-NLS-1$
1274 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1275 mClearAction.item.addSelectionListener(new SelectionAdapter() {
1277 public void widgetSelected(SelectionEvent e) {
1282 new ToolItem(toolBar, SWT.SEPARATOR);
1284 mExportAction = new ToolItemAction(toolBar, SWT.PUSH);
1285 mExportAction.item.setToolTipText("Export Selection As Text...");
1286 mExportAction.item.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, mDisplay,
1287 "save.png", //$NON-NLS-1$
1288 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1289 mExportAction.item.addSelectionListener(new SelectionAdapter() {
1291 public void widgetSelected(SelectionEvent e) {
1299 // now create the log view
1300 mLogPanel = new LogPanel(new ImageLoader(LogPanel.class), colors, new FilterStorage(),
1301 LogPanel.FILTER_MANUAL);
1303 mLogPanel.setActions(mDeleteFilterAction, mEditFilterAction, mLogLevelActions);
1305 String colMode = prefs.getString(PrefsDialog.LOGCAT_COLUMN_MODE);
1306 if (PrefsDialog.LOGCAT_COLUMN_MODE_AUTO.equals(colMode)) {
1307 mLogPanel.setColumnMode(LogPanel.COLUMN_MODE_AUTO);
1310 String fontStr = PrefsDialog.getStore().getString(PrefsDialog.LOGCAT_FONT);
1311 if (fontStr != null) {
1313 FontData fdat = new FontData(fontStr);
1314 mLogPanel.setFont(new Font(display, fdat));
1315 } catch (IllegalArgumentException e) {
1316 // Looks like fontStr isn't a valid font representation.
1317 // We do nothing in this case, the logcat view will use the default font.
1318 } catch (SWTError e2) {
1319 // Looks like the Font() constructor failed.
1320 // We do nothing in this case, the logcat view will use the default font.
1324 mLogPanel.createPanel(comp);
1326 // and start the logcat
1327 mLogPanel.startLogCat(mCurrentDevice);
1331 * Create the contents of the left panel: a table of VMs.
1333 private void createLeftPanel(final Composite comp) {
1334 comp.setLayout(new GridLayout(1, false));
1335 ToolBar toolBar = new ToolBar(comp, SWT.HORIZONTAL | SWT.RIGHT | SWT.WRAP);
1336 toolBar.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
1337 createDevicePanelToolBar(toolBar);
1339 Composite c = new Composite(comp, SWT.NONE);
1340 c.setLayoutData(new GridData(GridData.FILL_BOTH));
1342 mDevicePanel = new DevicePanel(new ImageLoader(DevicePanel.class), true /* showPorts */);
1343 mDevicePanel.createPanel(c);
1345 // add ourselves to the device panel selection listener
1346 mDevicePanel.addSelectionListener(this);
1350 * Create the contents of the right panel: tabs with VM information.
1352 private void createRightPanel(final Composite comp) {
1354 TabFolder tabFolder;
1356 comp.setLayout(new FillLayout());
1358 tabFolder = new TabFolder(comp, SWT.NONE);
1360 for (int i = 0; i < mPanels.length; i++) {
1361 if (mPanels[i] != null) {
1362 item = new TabItem(tabFolder, SWT.NONE);
1363 item.setText(mPanelNames[i]);
1364 item.setToolTipText(mPanelTips[i]);
1365 item.setControl(mPanels[i].createPanel(tabFolder));
1369 // add the emulator control panel to the folders.
1370 item = new TabItem(tabFolder, SWT.NONE);
1371 item.setText("Emulator Control");
1372 item.setToolTipText("Emulator Control Panel");
1373 mEmulatorPanel = new EmulatorControlPanel(mDdmuiLibImageLoader);
1374 item.setControl(mEmulatorPanel.createPanel(tabFolder));
1376 // add the event log panel to the folders.
1377 item = new TabItem(tabFolder, SWT.NONE);
1378 item.setText("Event Log");
1379 item.setToolTipText("Event Log");
1381 // create the composite that will hold the toolbar and the event log panel.
1382 Composite eventLogTopComposite = new Composite(tabFolder, SWT.NONE);
1383 item.setControl(eventLogTopComposite);
1384 eventLogTopComposite.setLayout(new GridLayout(1, false));
1386 // create the toolbar and the actions
1387 ToolBar toolbar = new ToolBar(eventLogTopComposite, SWT.HORIZONTAL);
1388 toolbar.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
1390 ToolItemAction optionsAction = new ToolItemAction(toolbar, SWT.PUSH);
1391 optionsAction.item.setToolTipText("Opens the options panel");
1392 optionsAction.item.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, comp.getDisplay(),
1393 "edit.png", //$NON-NLS-1$
1394 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1396 ToolItemAction clearAction = new ToolItemAction(toolbar, SWT.PUSH);
1397 clearAction.item.setToolTipText("Clears the event log");
1398 clearAction.item.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, comp.getDisplay(),
1399 "clear.png", //$NON-NLS-1$
1400 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1402 new ToolItem(toolbar, SWT.SEPARATOR);
1404 ToolItemAction saveAction = new ToolItemAction(toolbar, SWT.PUSH);
1405 saveAction.item.setToolTipText("Saves the event log");
1406 saveAction.item.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, comp.getDisplay(),
1407 "save.png", //$NON-NLS-1$
1408 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1410 ToolItemAction loadAction = new ToolItemAction(toolbar, SWT.PUSH);
1411 loadAction.item.setToolTipText("Loads an event log");
1412 loadAction.item.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, comp.getDisplay(),
1413 "load.png", //$NON-NLS-1$
1414 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1416 ToolItemAction importBugAction = new ToolItemAction(toolbar, SWT.PUSH);
1417 importBugAction.item.setToolTipText("Imports a bug report");
1418 importBugAction.item.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, comp.getDisplay(),
1419 "importBug.png", //$NON-NLS-1$
1420 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1422 // create the event log panel
1423 mEventLogPanel = new EventLogPanel(mDdmuiLibImageLoader);
1425 // set the external actions
1426 mEventLogPanel.setActions(optionsAction, clearAction, saveAction, loadAction,
1430 mEventLogPanel.createPanel(eventLogTopComposite);
1433 private void createFileExplorer() {
1434 if (mExplorer == null) {
1435 mExplorerShell = new Shell(mDisplay);
1438 mExplorerShell.setLayout(new GridLayout(1, false));
1441 ToolBar toolBar = new ToolBar(mExplorerShell, SWT.HORIZONTAL);
1442 toolBar.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
1444 ToolItemAction pullAction = new ToolItemAction(toolBar, SWT.PUSH);
1445 pullAction.item.setToolTipText("Pull File from Device");
1446 Image image = mDdmuiLibImageLoader.loadImage("pull.png", mDisplay); //$NON-NLS-1$
1447 if (image != null) {
1448 pullAction.item.setImage(image);
1450 // this is for debugging purpose when the icon is missing
1451 pullAction.item.setText("Pull"); //$NON-NLS-1$
1454 ToolItemAction pushAction = new ToolItemAction(toolBar, SWT.PUSH);
1455 pushAction.item.setToolTipText("Push file onto Device");
1456 image = mDdmuiLibImageLoader.loadImage("push.png", mDisplay); //$NON-NLS-1$
1457 if (image != null) {
1458 pushAction.item.setImage(image);
1460 // this is for debugging purpose when the icon is missing
1461 pushAction.item.setText("Push"); //$NON-NLS-1$
1464 ToolItemAction deleteAction = new ToolItemAction(toolBar, SWT.PUSH);
1465 deleteAction.item.setToolTipText("Delete");
1466 image = mDdmuiLibImageLoader.loadImage("delete.png", mDisplay); //$NON-NLS-1$
1467 if (image != null) {
1468 deleteAction.item.setImage(image);
1470 // this is for debugging purpose when the icon is missing
1471 deleteAction.item.setText("Delete"); //$NON-NLS-1$
1475 mExplorer = new DeviceExplorer();
1477 mExplorer.setImages(mDdmuiLibImageLoader.loadImage("file.png", mDisplay), //$NON-NLS-1$
1478 mDdmuiLibImageLoader.loadImage("folder.png", mDisplay), //$NON-NLS-1$
1479 mDdmuiLibImageLoader.loadImage("android.png", mDisplay), //$NON-NLS-1$
1481 mExplorer.setActions(pushAction, pullAction, deleteAction);
1483 pullAction.item.addSelectionListener(new SelectionAdapter() {
1485 public void widgetSelected(SelectionEvent e) {
1486 mExplorer.pullSelection();
1489 pullAction.setEnabled(false);
1491 pushAction.item.addSelectionListener(new SelectionAdapter() {
1493 public void widgetSelected(SelectionEvent e) {
1494 mExplorer.pushIntoSelection();
1497 pushAction.setEnabled(false);
1499 deleteAction.item.addSelectionListener(new SelectionAdapter() {
1501 public void widgetSelected(SelectionEvent e) {
1502 mExplorer.deleteSelection();
1505 deleteAction.setEnabled(false);
1507 Composite parent = new Composite(mExplorerShell, SWT.NONE);
1508 parent.setLayoutData(new GridData(GridData.FILL_BOTH));
1510 mExplorer.createPanel(parent);
1511 mExplorer.switchDevice(mCurrentDevice);
1513 mExplorerShell.addShellListener(new ShellListener() {
1514 public void shellActivated(ShellEvent e) {
1518 public void shellClosed(ShellEvent e) {
1520 mExplorerShell = null;
1523 public void shellDeactivated(ShellEvent e) {
1527 public void shellDeiconified(ShellEvent e) {
1531 public void shellIconified(ShellEvent e) {
1536 mExplorerShell.pack();
1537 setExplorerSizeAndPosition(mExplorerShell);
1538 mExplorerShell.open();
1540 if (mExplorerShell != null) {
1541 mExplorerShell.forceActive();
1547 * Set the status line. TODO: make this a stack, so we can safely have
1548 * multiple things trying to set it all at once. Also specify an expiration?
1550 public void setStatusLine(final String str) {
1552 mDisplay.asyncExec(new Runnable() {
1554 doSetStatusLine(str);
1557 } catch (SWTException swte) {
1558 if (!mDisplay.isDisposed())
1563 private void doSetStatusLine(String str) {
1564 if (mStatusLine.isDisposed())
1567 if (!mStatusLine.getText().equals(str)) {
1568 mStatusLine.setText(str);
1570 // try { Thread.sleep(100); }
1571 // catch (InterruptedException ie) {}
1575 public void displayError(final String msg) {
1577 mDisplay.syncExec(new Runnable() {
1579 MessageDialog.openError(mDisplay.getActiveShell(), "Error",
1583 } catch (SWTException swte) {
1584 if (!mDisplay.isDisposed())
1589 private void enableButtons() {
1590 if (mCurrentClient != null) {
1591 mTBShowThreadUpdates.setSelection(mCurrentClient.isThreadUpdateEnabled());
1592 mTBShowThreadUpdates.setEnabled(true);
1593 mTBShowHeapUpdates.setSelection(mCurrentClient.isHeapUpdateEnabled());
1594 mTBShowHeapUpdates.setEnabled(true);
1595 mTBHalt.setEnabled(true);
1596 mTBCauseGc.setEnabled(true);
1598 ClientData data = mCurrentClient.getClientData();
1600 if (data.hasFeature(ClientData.FEATURE_HPROF)) {
1601 mTBDumpHprof.setEnabled(data.hasPendingHprofDump() == false);
1602 mTBDumpHprof.setToolTipText("Dump HPROF file");
1604 mTBDumpHprof.setEnabled(false);
1605 mTBDumpHprof.setToolTipText("Dump HPROF file (not supported by this VM)");
1608 if (data.hasFeature(ClientData.FEATURE_PROFILING)) {
1609 mTBProfiling.setEnabled(true);
1610 if (data.getMethodProfilingStatus() == MethodProfilingStatus.ON) {
1611 mTBProfiling.setToolTipText("Stop Method Profiling");
1612 mTBProfiling.setImage(mTracingStopImage);
1614 mTBProfiling.setToolTipText("Start Method Profiling");
1615 mTBProfiling.setImage(mTracingStartImage);
1618 mTBProfiling.setEnabled(false);
1619 mTBProfiling.setImage(mTracingStartImage);
1620 mTBProfiling.setToolTipText("Start Method Profiling (not supported by this VM)");
1623 // list is empty, disable these
1624 mTBShowThreadUpdates.setSelection(false);
1625 mTBShowThreadUpdates.setEnabled(false);
1626 mTBShowHeapUpdates.setSelection(false);
1627 mTBShowHeapUpdates.setEnabled(false);
1628 mTBHalt.setEnabled(false);
1629 mTBCauseGc.setEnabled(false);
1631 mTBDumpHprof.setEnabled(false);
1632 mTBDumpHprof.setToolTipText("Dump HPROF file");
1634 mTBProfiling.setEnabled(false);
1635 mTBProfiling.setImage(mTracingStartImage);
1636 mTBProfiling.setToolTipText("Start Method Profiling");
1641 * Sent when a new {@link IDevice} and {@link Client} are selected.
1642 * @param selectedDevice the selected device. If null, no devices are selected.
1643 * @param selectedClient The selected client. If null, no clients are selected.
1645 * @see IUiSelectionListener
1647 public void selectionChanged(IDevice selectedDevice, Client selectedClient) {
1648 if (mCurrentDevice != selectedDevice) {
1649 mCurrentDevice = selectedDevice;
1650 for (TablePanel panel : mPanels) {
1651 if (panel != null) {
1652 panel.deviceSelected(mCurrentDevice);
1656 mEmulatorPanel.deviceSelected(mCurrentDevice);
1657 mLogPanel.deviceSelected(mCurrentDevice);
1658 if (mEventLogPanel != null) {
1659 mEventLogPanel.deviceSelected(mCurrentDevice);
1662 if (mExplorer != null) {
1663 mExplorer.switchDevice(mCurrentDevice);
1667 if (mCurrentClient != selectedClient) {
1668 AndroidDebugBridge.getBridge().setSelectedClient(selectedClient);
1669 mCurrentClient = selectedClient;
1670 for (TablePanel panel : mPanels) {
1671 if (panel != null) {
1672 panel.clientSelected(mCurrentClient);
1680 public void clientChanged(Client client, int changeMask) {
1681 if ((changeMask & Client.CHANGE_METHOD_PROFILING_STATUS) ==
1682 Client.CHANGE_METHOD_PROFILING_STATUS) {
1683 if (mCurrentClient == client) {
1684 mDisplay.asyncExec(new Runnable() {
1686 // force refresh of the button enabled state.