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.
408 public void runUI() {
409 Display.setAppName("ddms");
410 mDisplay = new Display();
411 final Shell shell = new Shell(mDisplay);
413 // create the image loaders for DDMS and DDMUILIB
414 mDdmsImageLoader = new ImageLoader(this.getClass());
415 mDdmuiLibImageLoader = new ImageLoader(DevicePanel.class);
417 shell.setImage(ImageHelper.loadImage(mDdmsImageLoader, mDisplay,
418 "ddms-icon.png", //$NON-NLS-1$
421 Log.setLogOutput(new ILogOutput() {
422 public void printAndPromptLog(final LogLevel logLevel, final String tag,
423 final String message) {
424 Log.printLog(logLevel, tag, message);
425 // dialog box only run in UI thread..
426 mDisplay.asyncExec(new Runnable() {
428 Shell shell = mDisplay.getActiveShell();
429 if (logLevel == LogLevel.ERROR) {
430 MessageDialog.openError(shell, tag, message);
432 MessageDialog.openWarning(shell, tag, message);
438 public void printLog(LogLevel logLevel, String tag, String message) {
439 Log.printLog(logLevel, tag, message);
443 // set the handler for hprof dump
444 ClientData.setHprofDumpHandler(new HProfHandler(shell));
445 ClientData.setMethodProfilingHandler(new MethodProfilingHandler(shell));
447 // [try to] ensure ADB is running
448 String adbLocation = System.getProperty("com.android.ddms.bindir"); //$NON-NLS-1$
449 if (adbLocation != null && adbLocation.length() != 0) {
450 adbLocation += File.separator + "adb"; //$NON-NLS-1$
452 adbLocation = "adb"; //$NON-NLS-1$
455 AndroidDebugBridge.init(true /* debugger support */);
456 AndroidDebugBridge.createBridge(adbLocation, true /* forceNewBridge */);
458 // we need to listen to client change to be notified of client status (profiling) change
459 AndroidDebugBridge.addClientChangeListener(this);
461 shell.setText("Dalvik Debug Monitor");
462 setConfirmClose(shell);
464 createWidgets(shell);
467 setSizeAndPosition(shell);
470 Log.d("ddms", "UI is up");
472 while (!shell.isDisposed()) {
473 if (!mDisplay.readAndDispatch())
476 mLogPanel.stopLogCat(true);
478 mDevicePanel.dispose();
479 for (TablePanel panel : mPanels) {
486 Log.d("ddms", "UI is down");
490 * Set the size and position of the main window from the preference, and
491 * setup listeners for control events (resize/move of the window)
493 private void setSizeAndPosition(final Shell shell) {
494 shell.setMinimumSize(400, 200);
496 // get the x/y and w/h from the prefs
497 PreferenceStore prefs = PrefsDialog.getStore();
498 int x = prefs.getInt(PrefsDialog.SHELL_X);
499 int y = prefs.getInt(PrefsDialog.SHELL_Y);
500 int w = prefs.getInt(PrefsDialog.SHELL_WIDTH);
501 int h = prefs.getInt(PrefsDialog.SHELL_HEIGHT);
503 // check that we're not out of the display area
504 Rectangle rect = mDisplay.getClientArea();
505 // first check the width/height
506 if (w > rect.width) {
508 prefs.setValue(PrefsDialog.SHELL_WIDTH, rect.width);
510 if (h > rect.height) {
512 prefs.setValue(PrefsDialog.SHELL_HEIGHT, rect.height);
514 // then check x. Make sure the left corner is in the screen
517 prefs.setValue(PrefsDialog.SHELL_X, rect.x);
518 } else if (x >= rect.x + rect.width) {
519 x = rect.x + rect.width - w;
520 prefs.setValue(PrefsDialog.SHELL_X, rect.x);
522 // then check y. Make sure the left corner is in the screen
525 prefs.setValue(PrefsDialog.SHELL_Y, rect.y);
526 } else if (y >= rect.y + rect.height) {
527 y = rect.y + rect.height - h;
528 prefs.setValue(PrefsDialog.SHELL_Y, rect.y);
531 // now we can set the location/size
532 shell.setBounds(x, y, w, h);
534 // add listener for resize/move
535 shell.addControlListener(new ControlListener() {
536 public void controlMoved(ControlEvent e) {
538 Rectangle rect = shell.getBounds();
539 // store in pref file
540 PreferenceStore prefs = PrefsDialog.getStore();
541 prefs.setValue(PrefsDialog.SHELL_X, rect.x);
542 prefs.setValue(PrefsDialog.SHELL_Y, rect.y);
545 public void controlResized(ControlEvent e) {
547 Rectangle rect = shell.getBounds();
548 // store in pref file
549 PreferenceStore prefs = PrefsDialog.getStore();
550 prefs.setValue(PrefsDialog.SHELL_WIDTH, rect.width);
551 prefs.setValue(PrefsDialog.SHELL_HEIGHT, rect.height);
557 * Set the size and position of the file explorer window from the
558 * preference, and setup listeners for control events (resize/move of
561 private void setExplorerSizeAndPosition(final Shell shell) {
562 shell.setMinimumSize(400, 200);
564 // get the x/y and w/h from the prefs
565 PreferenceStore prefs = PrefsDialog.getStore();
566 int x = prefs.getInt(PrefsDialog.EXPLORER_SHELL_X);
567 int y = prefs.getInt(PrefsDialog.EXPLORER_SHELL_Y);
568 int w = prefs.getInt(PrefsDialog.EXPLORER_SHELL_WIDTH);
569 int h = prefs.getInt(PrefsDialog.EXPLORER_SHELL_HEIGHT);
571 // check that we're not out of the display area
572 Rectangle rect = mDisplay.getClientArea();
573 // first check the width/height
574 if (w > rect.width) {
576 prefs.setValue(PrefsDialog.EXPLORER_SHELL_WIDTH, rect.width);
578 if (h > rect.height) {
580 prefs.setValue(PrefsDialog.EXPLORER_SHELL_HEIGHT, rect.height);
582 // then check x. Make sure the left corner is in the screen
585 prefs.setValue(PrefsDialog.EXPLORER_SHELL_X, rect.x);
586 } else if (x >= rect.x + rect.width) {
587 x = rect.x + rect.width - w;
588 prefs.setValue(PrefsDialog.EXPLORER_SHELL_X, rect.x);
590 // then check y. Make sure the left corner is in the screen
593 prefs.setValue(PrefsDialog.EXPLORER_SHELL_Y, rect.y);
594 } else if (y >= rect.y + rect.height) {
595 y = rect.y + rect.height - h;
596 prefs.setValue(PrefsDialog.EXPLORER_SHELL_Y, rect.y);
599 // now we can set the location/size
600 shell.setBounds(x, y, w, h);
602 // add listener for resize/move
603 shell.addControlListener(new ControlListener() {
604 public void controlMoved(ControlEvent e) {
606 Rectangle rect = shell.getBounds();
607 // store in pref file
608 PreferenceStore prefs = PrefsDialog.getStore();
609 prefs.setValue(PrefsDialog.EXPLORER_SHELL_X, rect.x);
610 prefs.setValue(PrefsDialog.EXPLORER_SHELL_Y, rect.y);
613 public void controlResized(ControlEvent e) {
615 Rectangle rect = shell.getBounds();
616 // store in pref file
617 PreferenceStore prefs = PrefsDialog.getStore();
618 prefs.setValue(PrefsDialog.EXPLORER_SHELL_WIDTH, rect.width);
619 prefs.setValue(PrefsDialog.EXPLORER_SHELL_HEIGHT, rect.height);
625 * Set the confirm-before-close dialog. TODO: enable/disable in prefs. TODO:
626 * is there any point in having this?
628 private void setConfirmClose(final Shell shell) {
632 shell.addListener(SWT.Close, new Listener() {
633 public void handleEvent(Event event) {
634 int style = SWT.APPLICATION_MODAL | SWT.YES | SWT.NO;
635 MessageBox msgBox = new MessageBox(shell, style);
636 msgBox.setText("Confirm...");
637 msgBox.setMessage("Close DDM?");
638 event.doit = (msgBox.open() == SWT.YES);
644 * Create the menu bar and items.
646 private void createMenus(final Shell shell) {
648 Menu menuBar = new Menu(shell, SWT.BAR);
650 // create top-level items
651 MenuItem fileItem = new MenuItem(menuBar, SWT.CASCADE);
652 fileItem.setText("&File");
653 MenuItem editItem = new MenuItem(menuBar, SWT.CASCADE);
654 editItem.setText("&Edit");
655 MenuItem actionItem = new MenuItem(menuBar, SWT.CASCADE);
656 actionItem.setText("&Actions");
657 MenuItem deviceItem = new MenuItem(menuBar, SWT.CASCADE);
658 deviceItem.setText("&Device");
659 MenuItem helpItem = new MenuItem(menuBar, SWT.CASCADE);
660 helpItem.setText("&Help");
662 // create top-level menus
663 Menu fileMenu = new Menu(menuBar);
664 fileItem.setMenu(fileMenu);
665 Menu editMenu = new Menu(menuBar);
666 editItem.setMenu(editMenu);
667 Menu actionMenu = new Menu(menuBar);
668 actionItem.setMenu(actionMenu);
669 Menu deviceMenu = new Menu(menuBar);
670 deviceItem.setMenu(deviceMenu);
671 Menu helpMenu = new Menu(menuBar);
672 helpItem.setMenu(helpMenu);
676 // create File menu items
677 item = new MenuItem(fileMenu, SWT.NONE);
678 item.setText("&Preferences...");
679 item.addSelectionListener(new SelectionAdapter() {
681 public void widgetSelected(SelectionEvent e) {
682 PrefsDialog.run(shell);
686 item = new MenuItem(fileMenu, SWT.NONE);
687 item.setText("&Static Port Configuration...");
688 item.addSelectionListener(new SelectionAdapter() {
690 public void widgetSelected(SelectionEvent e) {
691 StaticPortConfigDialog dlg = new StaticPortConfigDialog(shell);
696 new MenuItem(fileMenu, SWT.SEPARATOR);
698 item = new MenuItem(fileMenu, SWT.NONE);
699 item.setText("E&xit\tCtrl-Q");
700 item.setAccelerator('Q' | SWT.CONTROL);
701 item.addSelectionListener(new SelectionAdapter() {
703 public void widgetSelected(SelectionEvent e) {
708 // create edit menu items
709 mCopyMenuItem = new MenuItem(editMenu, SWT.NONE);
710 mCopyMenuItem.setText("&Copy\tCtrl-C");
711 mCopyMenuItem.setAccelerator('C' | SWT.COMMAND);
712 mCopyMenuItem.addSelectionListener(new SelectionAdapter() {
714 public void widgetSelected(SelectionEvent e) {
715 mTableListener.copy(mClipboard);
719 new MenuItem(editMenu, SWT.SEPARATOR);
721 mSelectAllMenuItem = new MenuItem(editMenu, SWT.NONE);
722 mSelectAllMenuItem.setText("Select &All\tCtrl-A");
723 mSelectAllMenuItem.setAccelerator('A' | SWT.COMMAND);
724 mSelectAllMenuItem.addSelectionListener(new SelectionAdapter() {
726 public void widgetSelected(SelectionEvent e) {
727 mTableListener.selectAll();
731 // create Action menu items
732 // TODO: this should come with a confirmation dialog
733 final MenuItem actionHaltItem = new MenuItem(actionMenu, SWT.NONE);
734 actionHaltItem.setText("&Halt VM");
735 actionHaltItem.addSelectionListener(new SelectionAdapter() {
737 public void widgetSelected(SelectionEvent e) {
738 mDevicePanel.killSelectedClient();
742 final MenuItem actionCauseGcItem = new MenuItem(actionMenu, SWT.NONE);
743 actionCauseGcItem.setText("Cause &GC");
744 actionCauseGcItem.addSelectionListener(new SelectionAdapter() {
746 public void widgetSelected(SelectionEvent e) {
747 mDevicePanel.forceGcOnSelectedClient();
751 // configure Action items based on current state
752 actionMenu.addMenuListener(new MenuAdapter() {
754 public void menuShown(MenuEvent e) {
755 actionHaltItem.setEnabled(mTBHalt.getEnabled() && mCurrentClient != null);
756 actionCauseGcItem.setEnabled(mTBCauseGc.getEnabled() && mCurrentClient != null);
760 // create Device menu items
761 final MenuItem screenShotItem = new MenuItem(deviceMenu, SWT.NONE);
762 screenShotItem.setText("&Screen capture...\tCTrl-S");
763 screenShotItem.setAccelerator('S' | SWT.CONTROL);
764 screenShotItem.addSelectionListener(new SelectionAdapter() {
766 public void widgetSelected(SelectionEvent e) {
767 if (mCurrentDevice != null) {
768 ScreenShotDialog dlg = new ScreenShotDialog(shell);
769 dlg.open(mCurrentDevice);
774 new MenuItem(deviceMenu, SWT.SEPARATOR);
776 final MenuItem explorerItem = new MenuItem(deviceMenu, SWT.NONE);
777 explorerItem.setText("File Explorer...");
778 explorerItem.addSelectionListener(new SelectionAdapter() {
780 public void widgetSelected(SelectionEvent e) {
781 createFileExplorer();
785 new MenuItem(deviceMenu, SWT.SEPARATOR);
787 final MenuItem processItem = new MenuItem(deviceMenu, SWT.NONE);
788 processItem.setText("Show &process status...");
789 processItem.addSelectionListener(new SelectionAdapter() {
791 public void widgetSelected(SelectionEvent e) {
792 DeviceCommandDialog dlg;
793 dlg = new DeviceCommandDialog("ps -x", "ps-x.txt", shell);
794 dlg.open(mCurrentDevice);
798 final MenuItem deviceStateItem = new MenuItem(deviceMenu, SWT.NONE);
799 deviceStateItem.setText("Dump &device state...");
800 deviceStateItem.addSelectionListener(new SelectionAdapter() {
802 public void widgetSelected(SelectionEvent e) {
803 DeviceCommandDialog dlg;
804 dlg = new DeviceCommandDialog("/system/bin/dumpstate /proc/self/fd/0",
805 "device-state.txt", shell);
806 dlg.open(mCurrentDevice);
810 final MenuItem appStateItem = new MenuItem(deviceMenu, SWT.NONE);
811 appStateItem.setText("Dump &app state...");
812 appStateItem.setEnabled(false);
813 appStateItem.addSelectionListener(new SelectionAdapter() {
815 public void widgetSelected(SelectionEvent e) {
816 DeviceCommandDialog dlg;
817 dlg = new DeviceCommandDialog("dumpsys", "app-state.txt", shell);
818 dlg.open(mCurrentDevice);
822 final MenuItem radioStateItem = new MenuItem(deviceMenu, SWT.NONE);
823 radioStateItem.setText("Dump &radio state...");
824 radioStateItem.addSelectionListener(new SelectionAdapter() {
826 public void widgetSelected(SelectionEvent e) {
827 DeviceCommandDialog dlg;
828 dlg = new DeviceCommandDialog(
829 "cat /data/logs/radio.4 /data/logs/radio.3"
830 + " /data/logs/radio.2 /data/logs/radio.1"
831 + " /data/logs/radio",
832 "radio-state.txt", shell);
833 dlg.open(mCurrentDevice);
837 final MenuItem logCatItem = new MenuItem(deviceMenu, SWT.NONE);
838 logCatItem.setText("Run &logcat...");
839 logCatItem.addSelectionListener(new SelectionAdapter() {
841 public void widgetSelected(SelectionEvent e) {
842 DeviceCommandDialog dlg;
843 dlg = new DeviceCommandDialog("logcat '*:d jdwp:w'", "log.txt",
845 dlg.open(mCurrentDevice);
849 // configure Action items based on current state
850 deviceMenu.addMenuListener(new MenuAdapter() {
852 public void menuShown(MenuEvent e) {
853 boolean deviceEnabled = mCurrentDevice != null;
854 screenShotItem.setEnabled(deviceEnabled);
855 explorerItem.setEnabled(deviceEnabled);
856 processItem.setEnabled(deviceEnabled);
857 deviceStateItem.setEnabled(deviceEnabled);
858 appStateItem.setEnabled(deviceEnabled);
859 radioStateItem.setEnabled(deviceEnabled);
860 logCatItem.setEnabled(deviceEnabled);
864 // create Help menu items
865 item = new MenuItem(helpMenu, SWT.NONE);
866 item.setText("&Contents...");
867 item.addSelectionListener(new SelectionAdapter() {
869 public void widgetSelected(SelectionEvent e) {
870 int style = SWT.APPLICATION_MODAL | SWT.OK;
871 MessageBox msgBox = new MessageBox(shell, style);
872 msgBox.setText("Help!");
873 msgBox.setMessage("Help wanted.");
878 new MenuItem(helpMenu, SWT.SEPARATOR);
880 item = new MenuItem(helpMenu, SWT.NONE);
881 item.setText("&About...");
882 item.addSelectionListener(new SelectionAdapter() {
884 public void widgetSelected(SelectionEvent e) {
885 AboutDialog dlg = new AboutDialog(shell);
890 // tell the shell to use this menu
891 shell.setMenuBar(menuBar);
895 * Create the widgets in the main application window. The basic layout is a
896 * two-panel sash, with a scrolling list of VMs on the left and detailed
897 * output for a single VM on the right.
899 private void createWidgets(final Shell shell) {
900 Color darkGray = shell.getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY);
903 * Create three areas: tool bar, split panels, status line
905 shell.setLayout(new GridLayout(1, false));
908 final Composite panelArea = new Composite(shell, SWT.BORDER);
910 // make the panel area absorb all space
911 panelArea.setLayoutData(new GridData(GridData.FILL_BOTH));
914 mStatusLine = new Label(shell, SWT.NONE);
916 // make status line extend all the way across
917 mStatusLine.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
919 mStatusLine.setText("Initializing...");
922 * Configure the split-panel area.
924 final PreferenceStore prefs = PrefsDialog.getStore();
926 Composite topPanel = new Composite(panelArea, SWT.NONE);
927 final Sash sash = new Sash(panelArea, SWT.HORIZONTAL);
928 sash.setBackground(darkGray);
929 Composite bottomPanel = new Composite(panelArea, SWT.NONE);
931 panelArea.setLayout(new FormLayout());
933 createTopPanel(topPanel, darkGray);
934 createBottomPanel(bottomPanel);
937 FormData data = new FormData();
938 data.top = new FormAttachment(0, 0);
939 data.bottom = new FormAttachment(sash, 0);
940 data.left = new FormAttachment(0, 0);
941 data.right = new FormAttachment(100, 0);
942 topPanel.setLayoutData(data);
944 final FormData sashData = new FormData();
945 if (prefs != null && prefs.contains(PREFERENCE_LOGSASH)) {
946 sashData.top = new FormAttachment(0, prefs.getInt(
947 PREFERENCE_LOGSASH));
949 sashData.top = new FormAttachment(50,0); // 50% across
951 sashData.left = new FormAttachment(0, 0);
952 sashData.right = new FormAttachment(100, 0);
953 sash.setLayoutData(sashData);
955 data = new FormData();
956 data.top = new FormAttachment(sash, 0);
957 data.bottom = new FormAttachment(100, 0);
958 data.left = new FormAttachment(0, 0);
959 data.right = new FormAttachment(100, 0);
960 bottomPanel.setLayoutData(data);
962 // allow resizes, but cap at minPanelWidth
963 sash.addListener(SWT.Selection, new Listener() {
964 public void handleEvent(Event e) {
965 Rectangle sashRect = sash.getBounds();
966 Rectangle panelRect = panelArea.getClientArea();
967 int bottom = panelRect.height - sashRect.height - 100;
968 e.y = Math.max(Math.min(e.y, bottom), 100);
969 if (e.y != sashRect.y) {
970 sashData.top = new FormAttachment(0, e.y);
971 prefs.setValue(PREFERENCE_LOGSASH, e.y);
977 // add a global focus listener for all the tables
978 mTableListener = new TableFocusListener();
980 // now set up the listener in the various panels
981 mLogPanel.setTableFocusListener(mTableListener);
982 mEventLogPanel.setTableFocusListener(mTableListener);
983 for (TablePanel p : mPanels) {
985 p.setTableFocusListener(mTableListener);
989 mStatusLine.setText("");
993 * Populate the tool bar.
995 private void createDevicePanelToolBar(ToolBar toolBar) {
996 Display display = toolBar.getDisplay();
998 // add "show heap updates" button
999 mTBShowHeapUpdates = new ToolItem(toolBar, SWT.CHECK);
1000 mTBShowHeapUpdates.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, display,
1001 DevicePanel.ICON_HEAP, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1002 mTBShowHeapUpdates.setToolTipText("Show heap updates");
1003 mTBShowHeapUpdates.setEnabled(false);
1004 mTBShowHeapUpdates.addSelectionListener(new SelectionAdapter() {
1006 public void widgetSelected(SelectionEvent e) {
1007 if (mCurrentClient != null) {
1008 // boolean status = ((ToolItem)e.item).getSelection();
1009 // invert previous state
1010 boolean enable = !mCurrentClient.isHeapUpdateEnabled();
1011 mCurrentClient.setHeapUpdateEnabled(enable);
1013 e.doit = false; // this has no effect?
1018 // add "dump HPROF" button
1019 mTBDumpHprof = new ToolItem(toolBar, SWT.PUSH);
1020 mTBDumpHprof.setToolTipText("Dump HPROF file");
1021 mTBDumpHprof.setEnabled(false);
1022 mTBDumpHprof.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, display,
1023 DevicePanel.ICON_HPROF, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1024 mTBDumpHprof.addSelectionListener(new SelectionAdapter() {
1026 public void widgetSelected(SelectionEvent e) {
1027 mDevicePanel.dumpHprof();
1029 // this will make sure the dump hprof button is disabled for the current selection
1030 // as the client is already dumping an hprof file
1035 // add "cause GC" button
1036 mTBCauseGc = new ToolItem(toolBar, SWT.PUSH);
1037 mTBCauseGc.setToolTipText("Cause an immediate GC");
1038 mTBCauseGc.setEnabled(false);
1039 mTBCauseGc.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, display,
1040 DevicePanel.ICON_GC, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1041 mTBCauseGc.addSelectionListener(new SelectionAdapter() {
1043 public void widgetSelected(SelectionEvent e) {
1044 mDevicePanel.forceGcOnSelectedClient();
1048 new ToolItem(toolBar, SWT.SEPARATOR);
1050 // add "show thread updates" button
1051 mTBShowThreadUpdates = new ToolItem(toolBar, SWT.CHECK);
1052 mTBShowThreadUpdates.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, display,
1053 DevicePanel.ICON_THREAD, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1054 mTBShowThreadUpdates.setToolTipText("Show thread updates");
1055 mTBShowThreadUpdates.setEnabled(false);
1056 mTBShowThreadUpdates.addSelectionListener(new SelectionAdapter() {
1058 public void widgetSelected(SelectionEvent e) {
1059 if (mCurrentClient != null) {
1060 // boolean status = ((ToolItem)e.item).getSelection();
1061 // invert previous state
1062 boolean enable = !mCurrentClient.isThreadUpdateEnabled();
1064 mCurrentClient.setThreadUpdateEnabled(enable);
1066 e.doit = false; // this has no effect?
1071 // add a start/stop method tracing
1072 mTracingStartImage = ImageHelper.loadImage(mDdmuiLibImageLoader, display,
1073 DevicePanel.ICON_TRACING_START,
1074 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null);
1075 mTracingStopImage = ImageHelper.loadImage(mDdmuiLibImageLoader, display,
1076 DevicePanel.ICON_TRACING_STOP,
1077 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null);
1078 mTBProfiling = new ToolItem(toolBar, SWT.PUSH);
1079 mTBProfiling.setToolTipText("Start Method Profiling");
1080 mTBProfiling.setEnabled(false);
1081 mTBProfiling.setImage(mTracingStartImage);
1082 mTBProfiling.addSelectionListener(new SelectionAdapter() {
1084 public void widgetSelected(SelectionEvent e) {
1085 mDevicePanel.toggleMethodProfiling();
1089 new ToolItem(toolBar, SWT.SEPARATOR);
1091 // add "kill VM" button; need to make this visually distinct from
1092 // the status update buttons
1093 mTBHalt = new ToolItem(toolBar, SWT.PUSH);
1094 mTBHalt.setToolTipText("Halt the target VM");
1095 mTBHalt.setEnabled(false);
1096 mTBHalt.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, display,
1097 DevicePanel.ICON_HALT, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1098 mTBHalt.addSelectionListener(new SelectionAdapter() {
1100 public void widgetSelected(SelectionEvent e) {
1101 mDevicePanel.killSelectedClient();
1108 private void createTopPanel(final Composite comp, Color darkGray) {
1109 final PreferenceStore prefs = PrefsDialog.getStore();
1111 comp.setLayout(new FormLayout());
1113 Composite leftPanel = new Composite(comp, SWT.NONE);
1114 final Sash sash = new Sash(comp, SWT.VERTICAL);
1115 sash.setBackground(darkGray);
1116 Composite rightPanel = new Composite(comp, SWT.NONE);
1118 createLeftPanel(leftPanel);
1119 createRightPanel(rightPanel);
1121 FormData data = new FormData();
1122 data.top = new FormAttachment(0, 0);
1123 data.bottom = new FormAttachment(100, 0);
1124 data.left = new FormAttachment(0, 0);
1125 data.right = new FormAttachment(sash, 0);
1126 leftPanel.setLayoutData(data);
1128 final FormData sashData = new FormData();
1129 sashData.top = new FormAttachment(0, 0);
1130 sashData.bottom = new FormAttachment(100, 0);
1131 if (prefs != null && prefs.contains(PREFERENCE_SASH)) {
1132 sashData.left = new FormAttachment(0, prefs.getInt(
1135 // position the sash 380 from the right instead of x% (done by using
1136 // FormAttachment(x, 0)) in order to keep the sash at the same
1138 // from the left when the window is resized.
1139 // 380px is just enough to display the left table with no horizontal
1140 // scrollbar with the default font.
1141 sashData.left = new FormAttachment(0, 380);
1143 sash.setLayoutData(sashData);
1145 data = new FormData();
1146 data.top = new FormAttachment(0, 0);
1147 data.bottom = new FormAttachment(100, 0);
1148 data.left = new FormAttachment(sash, 0);
1149 data.right = new FormAttachment(100, 0);
1150 rightPanel.setLayoutData(data);
1152 final int minPanelWidth = 60;
1154 // allow resizes, but cap at minPanelWidth
1155 sash.addListener(SWT.Selection, new Listener() {
1156 public void handleEvent(Event e) {
1157 Rectangle sashRect = sash.getBounds();
1158 Rectangle panelRect = comp.getClientArea();
1159 int right = panelRect.width - sashRect.width - minPanelWidth;
1160 e.x = Math.max(Math.min(e.x, right), minPanelWidth);
1161 if (e.x != sashRect.x) {
1162 sashData.left = new FormAttachment(0, e.x);
1163 prefs.setValue(PREFERENCE_SASH, e.x);
1170 private void createBottomPanel(final Composite comp) {
1171 final PreferenceStore prefs = PrefsDialog.getStore();
1174 Display display = comp.getDisplay();
1175 mClipboard = new Clipboard(display);
1177 LogColors colors = new LogColors();
1179 colors.infoColor = new Color(display, 0, 127, 0);
1180 colors.debugColor = new Color(display, 0, 0, 127);
1181 colors.errorColor = new Color(display, 255, 0, 0);
1182 colors.warningColor = new Color(display, 255, 127, 0);
1183 colors.verboseColor = new Color(display, 0, 0, 0);
1185 // set the preferences names
1186 LogPanel.PREFS_TIME = PREFS_COL_TIME;
1187 LogPanel.PREFS_LEVEL = PREFS_COL_LEVEL;
1188 LogPanel.PREFS_PID = PREFS_COL_PID;
1189 LogPanel.PREFS_TAG = PREFS_COL_TAG;
1190 LogPanel.PREFS_MESSAGE = PREFS_COL_MESSAGE;
1192 comp.setLayout(new GridLayout(1, false));
1194 ToolBar toolBar = new ToolBar(comp, SWT.HORIZONTAL);
1196 mCreateFilterAction = new ToolItemAction(toolBar, SWT.PUSH);
1197 mCreateFilterAction.item.setToolTipText("Create Filter");
1198 mCreateFilterAction.item.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, mDisplay,
1199 "add.png", //$NON-NLS-1$
1200 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1201 mCreateFilterAction.item.addSelectionListener(new SelectionAdapter() {
1203 public void widgetSelected(SelectionEvent e) {
1204 mLogPanel.addFilter();
1208 mEditFilterAction = new ToolItemAction(toolBar, SWT.PUSH);
1209 mEditFilterAction.item.setToolTipText("Edit Filter");
1210 mEditFilterAction.item.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, mDisplay,
1211 "edit.png", //$NON-NLS-1$
1212 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1213 mEditFilterAction.item.addSelectionListener(new SelectionAdapter() {
1215 public void widgetSelected(SelectionEvent e) {
1216 mLogPanel.editFilter();
1220 mDeleteFilterAction = new ToolItemAction(toolBar, SWT.PUSH);
1221 mDeleteFilterAction.item.setToolTipText("Delete Filter");
1222 mDeleteFilterAction.item.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, mDisplay,
1223 "delete.png", //$NON-NLS-1$
1224 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1225 mDeleteFilterAction.item.addSelectionListener(new SelectionAdapter() {
1227 public void widgetSelected(SelectionEvent e) {
1228 mLogPanel.deleteFilter();
1233 new ToolItem(toolBar, SWT.SEPARATOR);
1235 LogLevel[] levels = LogLevel.values();
1236 mLogLevelActions = new ToolItemAction[mLogLevelIcons.length];
1237 for (int i = 0 ; i < mLogLevelActions.length; i++) {
1238 String name = levels[i].getStringValue();
1239 final ToolItemAction newAction = new ToolItemAction(toolBar, SWT.CHECK);
1240 mLogLevelActions[i] = newAction;
1241 //newAction.item.setText(name);
1242 newAction.item.addSelectionListener(new SelectionAdapter() {
1244 public void widgetSelected(SelectionEvent e) {
1245 // disable the other actions and record current index
1246 for (int i = 0 ; i < mLogLevelActions.length; i++) {
1247 ToolItemAction a = mLogLevelActions[i];
1248 if (a == newAction) {
1251 // set the log level
1252 mLogPanel.setCurrentFilterLogLevel(i+2);
1254 a.setChecked(false);
1260 newAction.item.setToolTipText(name);
1261 newAction.item.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, mDisplay,
1263 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1266 new ToolItem(toolBar, SWT.SEPARATOR);
1268 mClearAction = new ToolItemAction(toolBar, SWT.PUSH);
1269 mClearAction.item.setToolTipText("Clear Log");
1271 mClearAction.item.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, mDisplay,
1272 "clear.png", //$NON-NLS-1$
1273 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1274 mClearAction.item.addSelectionListener(new SelectionAdapter() {
1276 public void widgetSelected(SelectionEvent e) {
1281 new ToolItem(toolBar, SWT.SEPARATOR);
1283 mExportAction = new ToolItemAction(toolBar, SWT.PUSH);
1284 mExportAction.item.setToolTipText("Export Selection As Text...");
1285 mExportAction.item.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, mDisplay,
1286 "save.png", //$NON-NLS-1$
1287 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1288 mExportAction.item.addSelectionListener(new SelectionAdapter() {
1290 public void widgetSelected(SelectionEvent e) {
1298 // now create the log view
1299 mLogPanel = new LogPanel(new ImageLoader(LogPanel.class), colors, new FilterStorage(),
1300 LogPanel.FILTER_MANUAL);
1302 mLogPanel.setActions(mDeleteFilterAction, mEditFilterAction, mLogLevelActions);
1304 String colMode = prefs.getString(PrefsDialog.LOGCAT_COLUMN_MODE);
1305 if (PrefsDialog.LOGCAT_COLUMN_MODE_AUTO.equals(colMode)) {
1306 mLogPanel.setColumnMode(LogPanel.COLUMN_MODE_AUTO);
1309 String fontStr = PrefsDialog.getStore().getString(PrefsDialog.LOGCAT_FONT);
1310 if (fontStr != null) {
1312 FontData fdat = new FontData(fontStr);
1313 mLogPanel.setFont(new Font(display, fdat));
1314 } catch (IllegalArgumentException e) {
1315 // Looks like fontStr isn't a valid font representation.
1316 // We do nothing in this case, the logcat view will use the default font.
1317 } catch (SWTError e2) {
1318 // Looks like the Font() constructor failed.
1319 // We do nothing in this case, the logcat view will use the default font.
1323 mLogPanel.createPanel(comp);
1325 // and start the logcat
1326 mLogPanel.startLogCat(mCurrentDevice);
1330 * Create the contents of the left panel: a table of VMs.
1332 private void createLeftPanel(final Composite comp) {
1333 comp.setLayout(new GridLayout(1, false));
1334 ToolBar toolBar = new ToolBar(comp, SWT.HORIZONTAL | SWT.RIGHT | SWT.WRAP);
1335 toolBar.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
1336 createDevicePanelToolBar(toolBar);
1338 Composite c = new Composite(comp, SWT.NONE);
1339 c.setLayoutData(new GridData(GridData.FILL_BOTH));
1341 mDevicePanel = new DevicePanel(new ImageLoader(DevicePanel.class), true /* showPorts */);
1342 mDevicePanel.createPanel(c);
1344 // add ourselves to the device panel selection listener
1345 mDevicePanel.addSelectionListener(this);
1349 * Create the contents of the right panel: tabs with VM information.
1351 private void createRightPanel(final Composite comp) {
1353 TabFolder tabFolder;
1355 comp.setLayout(new FillLayout());
1357 tabFolder = new TabFolder(comp, SWT.NONE);
1359 for (int i = 0; i < mPanels.length; i++) {
1360 if (mPanels[i] != null) {
1361 item = new TabItem(tabFolder, SWT.NONE);
1362 item.setText(mPanelNames[i]);
1363 item.setToolTipText(mPanelTips[i]);
1364 item.setControl(mPanels[i].createPanel(tabFolder));
1368 // add the emulator control panel to the folders.
1369 item = new TabItem(tabFolder, SWT.NONE);
1370 item.setText("Emulator Control");
1371 item.setToolTipText("Emulator Control Panel");
1372 mEmulatorPanel = new EmulatorControlPanel(mDdmuiLibImageLoader);
1373 item.setControl(mEmulatorPanel.createPanel(tabFolder));
1375 // add the event log panel to the folders.
1376 item = new TabItem(tabFolder, SWT.NONE);
1377 item.setText("Event Log");
1378 item.setToolTipText("Event Log");
1380 // create the composite that will hold the toolbar and the event log panel.
1381 Composite eventLogTopComposite = new Composite(tabFolder, SWT.NONE);
1382 item.setControl(eventLogTopComposite);
1383 eventLogTopComposite.setLayout(new GridLayout(1, false));
1385 // create the toolbar and the actions
1386 ToolBar toolbar = new ToolBar(eventLogTopComposite, SWT.HORIZONTAL);
1387 toolbar.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
1389 ToolItemAction optionsAction = new ToolItemAction(toolbar, SWT.PUSH);
1390 optionsAction.item.setToolTipText("Opens the options panel");
1391 optionsAction.item.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, comp.getDisplay(),
1392 "edit.png", //$NON-NLS-1$
1393 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1395 ToolItemAction clearAction = new ToolItemAction(toolbar, SWT.PUSH);
1396 clearAction.item.setToolTipText("Clears the event log");
1397 clearAction.item.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, comp.getDisplay(),
1398 "clear.png", //$NON-NLS-1$
1399 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1401 new ToolItem(toolbar, SWT.SEPARATOR);
1403 ToolItemAction saveAction = new ToolItemAction(toolbar, SWT.PUSH);
1404 saveAction.item.setToolTipText("Saves the event log");
1405 saveAction.item.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, comp.getDisplay(),
1406 "save.png", //$NON-NLS-1$
1407 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1409 ToolItemAction loadAction = new ToolItemAction(toolbar, SWT.PUSH);
1410 loadAction.item.setToolTipText("Loads an event log");
1411 loadAction.item.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, comp.getDisplay(),
1412 "load.png", //$NON-NLS-1$
1413 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1415 ToolItemAction importBugAction = new ToolItemAction(toolbar, SWT.PUSH);
1416 importBugAction.item.setToolTipText("Imports a bug report");
1417 importBugAction.item.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, comp.getDisplay(),
1418 "importBug.png", //$NON-NLS-1$
1419 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1421 // create the event log panel
1422 mEventLogPanel = new EventLogPanel(mDdmuiLibImageLoader);
1424 // set the external actions
1425 mEventLogPanel.setActions(optionsAction, clearAction, saveAction, loadAction,
1429 mEventLogPanel.createPanel(eventLogTopComposite);
1432 private void createFileExplorer() {
1433 if (mExplorer == null) {
1434 mExplorerShell = new Shell(mDisplay);
1437 mExplorerShell.setLayout(new GridLayout(1, false));
1440 ToolBar toolBar = new ToolBar(mExplorerShell, SWT.HORIZONTAL);
1441 toolBar.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
1443 ToolItemAction pullAction = new ToolItemAction(toolBar, SWT.PUSH);
1444 pullAction.item.setToolTipText("Pull File from Device");
1445 Image image = mDdmuiLibImageLoader.loadImage("pull.png", mDisplay); //$NON-NLS-1$
1446 if (image != null) {
1447 pullAction.item.setImage(image);
1449 // this is for debugging purpose when the icon is missing
1450 pullAction.item.setText("Pull"); //$NON-NLS-1$
1453 ToolItemAction pushAction = new ToolItemAction(toolBar, SWT.PUSH);
1454 pushAction.item.setToolTipText("Push file onto Device");
1455 image = mDdmuiLibImageLoader.loadImage("push.png", mDisplay); //$NON-NLS-1$
1456 if (image != null) {
1457 pushAction.item.setImage(image);
1459 // this is for debugging purpose when the icon is missing
1460 pushAction.item.setText("Push"); //$NON-NLS-1$
1463 ToolItemAction deleteAction = new ToolItemAction(toolBar, SWT.PUSH);
1464 deleteAction.item.setToolTipText("Delete");
1465 image = mDdmuiLibImageLoader.loadImage("delete.png", mDisplay); //$NON-NLS-1$
1466 if (image != null) {
1467 deleteAction.item.setImage(image);
1469 // this is for debugging purpose when the icon is missing
1470 deleteAction.item.setText("Delete"); //$NON-NLS-1$
1474 mExplorer = new DeviceExplorer();
1476 mExplorer.setImages(mDdmuiLibImageLoader.loadImage("file.png", mDisplay), //$NON-NLS-1$
1477 mDdmuiLibImageLoader.loadImage("folder.png", mDisplay), //$NON-NLS-1$
1478 mDdmuiLibImageLoader.loadImage("android.png", mDisplay), //$NON-NLS-1$
1480 mExplorer.setActions(pushAction, pullAction, deleteAction);
1482 pullAction.item.addSelectionListener(new SelectionAdapter() {
1484 public void widgetSelected(SelectionEvent e) {
1485 mExplorer.pullSelection();
1488 pullAction.setEnabled(false);
1490 pushAction.item.addSelectionListener(new SelectionAdapter() {
1492 public void widgetSelected(SelectionEvent e) {
1493 mExplorer.pushIntoSelection();
1496 pushAction.setEnabled(false);
1498 deleteAction.item.addSelectionListener(new SelectionAdapter() {
1500 public void widgetSelected(SelectionEvent e) {
1501 mExplorer.deleteSelection();
1504 deleteAction.setEnabled(false);
1506 Composite parent = new Composite(mExplorerShell, SWT.NONE);
1507 parent.setLayoutData(new GridData(GridData.FILL_BOTH));
1509 mExplorer.createPanel(parent);
1510 mExplorer.switchDevice(mCurrentDevice);
1512 mExplorerShell.addShellListener(new ShellListener() {
1513 public void shellActivated(ShellEvent e) {
1517 public void shellClosed(ShellEvent e) {
1519 mExplorerShell = null;
1522 public void shellDeactivated(ShellEvent e) {
1526 public void shellDeiconified(ShellEvent e) {
1530 public void shellIconified(ShellEvent e) {
1535 mExplorerShell.pack();
1536 setExplorerSizeAndPosition(mExplorerShell);
1537 mExplorerShell.open();
1539 if (mExplorerShell != null) {
1540 mExplorerShell.forceActive();
1546 * Set the status line. TODO: make this a stack, so we can safely have
1547 * multiple things trying to set it all at once. Also specify an expiration?
1549 public void setStatusLine(final String str) {
1551 mDisplay.asyncExec(new Runnable() {
1553 doSetStatusLine(str);
1556 } catch (SWTException swte) {
1557 if (!mDisplay.isDisposed())
1562 private void doSetStatusLine(String str) {
1563 if (mStatusLine.isDisposed())
1566 if (!mStatusLine.getText().equals(str)) {
1567 mStatusLine.setText(str);
1569 // try { Thread.sleep(100); }
1570 // catch (InterruptedException ie) {}
1574 public void displayError(final String msg) {
1576 mDisplay.syncExec(new Runnable() {
1578 MessageDialog.openError(mDisplay.getActiveShell(), "Error",
1582 } catch (SWTException swte) {
1583 if (!mDisplay.isDisposed())
1588 private void enableButtons() {
1589 if (mCurrentClient != null) {
1590 mTBShowThreadUpdates.setSelection(mCurrentClient.isThreadUpdateEnabled());
1591 mTBShowThreadUpdates.setEnabled(true);
1592 mTBShowHeapUpdates.setSelection(mCurrentClient.isHeapUpdateEnabled());
1593 mTBShowHeapUpdates.setEnabled(true);
1594 mTBHalt.setEnabled(true);
1595 mTBCauseGc.setEnabled(true);
1597 ClientData data = mCurrentClient.getClientData();
1599 if (data.hasFeature(ClientData.FEATURE_HPROF)) {
1600 mTBDumpHprof.setEnabled(data.hasPendingHprofDump() == false);
1601 mTBDumpHprof.setToolTipText("Dump HPROF file");
1603 mTBDumpHprof.setEnabled(false);
1604 mTBDumpHprof.setToolTipText("Dump HPROF file (not supported by this VM)");
1607 if (data.hasFeature(ClientData.FEATURE_PROFILING)) {
1608 mTBProfiling.setEnabled(true);
1609 if (data.getMethodProfilingStatus() == MethodProfilingStatus.ON) {
1610 mTBProfiling.setToolTipText("Stop Method Profiling");
1611 mTBProfiling.setImage(mTracingStopImage);
1613 mTBProfiling.setToolTipText("Start Method Profiling");
1614 mTBProfiling.setImage(mTracingStartImage);
1617 mTBProfiling.setEnabled(false);
1618 mTBProfiling.setImage(mTracingStartImage);
1619 mTBProfiling.setToolTipText("Start Method Profiling (not supported by this VM)");
1622 // list is empty, disable these
1623 mTBShowThreadUpdates.setSelection(false);
1624 mTBShowThreadUpdates.setEnabled(false);
1625 mTBShowHeapUpdates.setSelection(false);
1626 mTBShowHeapUpdates.setEnabled(false);
1627 mTBHalt.setEnabled(false);
1628 mTBCauseGc.setEnabled(false);
1630 mTBDumpHprof.setEnabled(false);
1631 mTBDumpHprof.setToolTipText("Dump HPROF file");
1633 mTBProfiling.setEnabled(false);
1634 mTBProfiling.setImage(mTracingStartImage);
1635 mTBProfiling.setToolTipText("Start Method Profiling");
1640 * Sent when a new {@link IDevice} and {@link Client} are selected.
1641 * @param selectedDevice the selected device. If null, no devices are selected.
1642 * @param selectedClient The selected client. If null, no clients are selected.
1644 * @see IUiSelectionListener
1646 public void selectionChanged(IDevice selectedDevice, Client selectedClient) {
1647 if (mCurrentDevice != selectedDevice) {
1648 mCurrentDevice = selectedDevice;
1649 for (TablePanel panel : mPanels) {
1650 if (panel != null) {
1651 panel.deviceSelected(mCurrentDevice);
1655 mEmulatorPanel.deviceSelected(mCurrentDevice);
1656 mLogPanel.deviceSelected(mCurrentDevice);
1657 if (mEventLogPanel != null) {
1658 mEventLogPanel.deviceSelected(mCurrentDevice);
1661 if (mExplorer != null) {
1662 mExplorer.switchDevice(mCurrentDevice);
1666 if (mCurrentClient != selectedClient) {
1667 AndroidDebugBridge.getBridge().setSelectedClient(selectedClient);
1668 mCurrentClient = selectedClient;
1669 for (TablePanel panel : mPanels) {
1670 if (panel != null) {
1671 panel.clientSelected(mCurrentClient);
1679 public void clientChanged(Client client, int changeMask) {
1680 if ((changeMask & Client.CHANGE_METHOD_PROFILING_STATUS) ==
1681 Client.CHANGE_METHOD_PROFILING_STATUS) {
1682 if (mCurrentClient == client) {
1683 mDisplay.asyncExec(new Runnable() {
1685 // force refresh of the button enabled state.