2 * Copyright (C) 2007 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.android.ddms;
19 import com.android.ddmlib.AndroidDebugBridge;
20 import com.android.ddmlib.Client;
21 import com.android.ddmlib.ClientData;
22 import com.android.ddmlib.IDevice;
23 import com.android.ddmlib.Log;
24 import com.android.ddmlib.SyncException;
25 import com.android.ddmlib.SyncService;
26 import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener;
27 import com.android.ddmlib.ClientData.IHprofDumpHandler;
28 import com.android.ddmlib.ClientData.MethodProfilingStatus;
29 import com.android.ddmlib.Log.ILogOutput;
30 import com.android.ddmlib.Log.LogLevel;
31 import com.android.ddmuilib.AllocationPanel;
32 import com.android.ddmuilib.DdmUiPreferences;
33 import com.android.ddmuilib.DevicePanel;
34 import com.android.ddmuilib.EmulatorControlPanel;
35 import com.android.ddmuilib.HeapPanel;
36 import com.android.ddmuilib.ITableFocusListener;
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.LogCatPanel;
51 import com.android.ddmuilib.logcat.LogCatReceiver;
52 import com.android.ddmuilib.logcat.LogColors;
53 import com.android.ddmuilib.logcat.LogFilter;
54 import com.android.ddmuilib.logcat.LogPanel;
55 import com.android.ddmuilib.logcat.LogPanel.ILogFilterStorageManager;
56 import com.android.menubar.IMenuBarCallback;
57 import com.android.menubar.IMenuBarEnhancer;
58 import com.android.menubar.IMenuBarEnhancer.MenuBarMode;
59 import com.android.menubar.MenuBarEnhancer;
61 import org.eclipse.jface.dialogs.MessageDialog;
62 import org.eclipse.jface.preference.IPreferenceStore;
63 import org.eclipse.jface.preference.PreferenceStore;
64 import org.eclipse.swt.SWT;
65 import org.eclipse.swt.SWTError;
66 import org.eclipse.swt.SWTException;
67 import org.eclipse.swt.dnd.Clipboard;
68 import org.eclipse.swt.events.ControlEvent;
69 import org.eclipse.swt.events.ControlListener;
70 import org.eclipse.swt.events.MenuAdapter;
71 import org.eclipse.swt.events.MenuEvent;
72 import org.eclipse.swt.events.SelectionAdapter;
73 import org.eclipse.swt.events.SelectionEvent;
74 import org.eclipse.swt.events.ShellEvent;
75 import org.eclipse.swt.events.ShellListener;
76 import org.eclipse.swt.graphics.Color;
77 import org.eclipse.swt.graphics.Font;
78 import org.eclipse.swt.graphics.FontData;
79 import org.eclipse.swt.graphics.Image;
80 import org.eclipse.swt.graphics.Rectangle;
81 import org.eclipse.swt.layout.FillLayout;
82 import org.eclipse.swt.layout.FormAttachment;
83 import org.eclipse.swt.layout.FormData;
84 import org.eclipse.swt.layout.FormLayout;
85 import org.eclipse.swt.layout.GridData;
86 import org.eclipse.swt.layout.GridLayout;
87 import org.eclipse.swt.widgets.Composite;
88 import org.eclipse.swt.widgets.Display;
89 import org.eclipse.swt.widgets.Event;
90 import org.eclipse.swt.widgets.Label;
91 import org.eclipse.swt.widgets.Listener;
92 import org.eclipse.swt.widgets.Menu;
93 import org.eclipse.swt.widgets.MenuItem;
94 import org.eclipse.swt.widgets.Sash;
95 import org.eclipse.swt.widgets.Shell;
96 import org.eclipse.swt.widgets.TabFolder;
97 import org.eclipse.swt.widgets.TabItem;
98 import org.eclipse.swt.widgets.ToolBar;
99 import org.eclipse.swt.widgets.ToolItem;
102 import java.util.ArrayList;
105 * This acts as the UI builder. This cannot be its own thread since this prevent using AWT in an
106 * SWT application. So this class mainly builds the ui, and manages communication between the panels
107 * when {@link IDevice} / {@link Client} selection changes.
109 public class UIThread implements IUiSelectionListener, IClientChangeListener {
110 private static final String APP_NAME = "DDMS";
113 * UI tab panel definitions. The constants here must match up with the array
114 * indices in mPanels. PANEL_CLIENT_LIST is a "virtual" panel representing
117 public static final int PANEL_CLIENT_LIST = -1;
119 public static final int PANEL_INFO = 0;
121 public static final int PANEL_THREAD = 1;
123 public static final int PANEL_HEAP = 2;
125 public static final int PANEL_NATIVE_HEAP = 3;
127 private static final int PANEL_ALLOCATIONS = 4;
129 private static final int PANEL_SYSINFO = 5;
131 private static final int PANEL_COUNT = 6;
133 /** Content is setup in the constructor */
134 private static TablePanel[] mPanels = new TablePanel[PANEL_COUNT];
136 private static final String[] mPanelNames = new String[] {
137 "Info", "Threads", "VM Heap", "Native Heap", "Allocation Tracker", "Sysinfo"
140 private static final String[] mPanelTips = new String[] {
141 "Client information", "Thread status", "VM heap status",
142 "Native heap status", "Allocation Tracker", "Sysinfo graphs"
145 private static final String PREFERENCE_LOGSASH =
146 "logSashLocation"; //$NON-NLS-1$
147 private static final String PREFERENCE_SASH =
148 "sashLocation"; //$NON-NLS-1$
150 private static final String PREFS_COL_TIME =
151 "logcat.time"; //$NON-NLS-1$
152 private static final String PREFS_COL_LEVEL =
153 "logcat.level"; //$NON-NLS-1$
154 private static final String PREFS_COL_PID =
155 "logcat.pid"; //$NON-NLS-1$
156 private static final String PREFS_COL_TAG =
157 "logcat.tag"; //$NON-NLS-1$
158 private static final String PREFS_COL_MESSAGE =
159 "logcat.message"; //$NON-NLS-1$
161 private static final String PREFS_FILTERS = "logcat.filter"; //$NON-NLS-1$
163 // singleton instance
164 private static UIThread mInstance = new UIThread();
167 private Display mDisplay;
169 // the table we show in the left-hand pane
170 private DevicePanel mDevicePanel;
172 private IDevice mCurrentDevice = null;
173 private Client mCurrentClient = null;
175 // status line at the bottom of the app window
176 private Label mStatusLine;
178 // some toolbar items we need to update
179 private ToolItem mTBShowThreadUpdates;
180 private ToolItem mTBShowHeapUpdates;
181 private ToolItem mTBHalt;
182 private ToolItem mTBCauseGc;
183 private ToolItem mTBDumpHprof;
184 private ToolItem mTBProfiling;
186 private final class FilterStorage implements ILogFilterStorageManager {
188 public LogFilter[] getFilterFromStore() {
189 String filterPrefs = PrefsDialog.getStore().getString(
192 // split in a string per filter
193 String[] filters = filterPrefs.split("\\|"); //$NON-NLS-1$
195 ArrayList<LogFilter> list =
196 new ArrayList<LogFilter>(filters.length);
198 for (String f : filters) {
199 if (f.length() > 0) {
200 LogFilter logFilter = new LogFilter();
201 if (logFilter.loadFromString(f)) {
207 return list.toArray(new LogFilter[list.size()]);
210 public void saveFilters(LogFilter[] filters) {
211 StringBuilder sb = new StringBuilder();
212 for (LogFilter f : filters) {
213 String filterString = f.toString();
214 sb.append(filterString);
218 PrefsDialog.getStore().setValue(PREFS_FILTERS, sb.toString());
221 public boolean requiresDefaultFilter() {
228 * Flag to indicate whether to use the old or the new logcat view. This is a
229 * temporary workaround that will be removed once the new view is complete.
231 private static final String USE_NEW_LOGCAT_VIEW =
232 System.getenv("ANDROID_USE_NEW_LOGCAT_VIEW");
233 private boolean useOldLogCatView() {
234 return USE_NEW_LOGCAT_VIEW == null;
237 private LogPanel mLogPanel; /* only valid when useOldLogCatView() == true */
238 private LogCatPanel mLogCatPanel; /* only valid when useOldLogCatView() == false */
240 private ToolItemAction mCreateFilterAction;
241 private ToolItemAction mDeleteFilterAction;
242 private ToolItemAction mEditFilterAction;
243 private ToolItemAction mExportAction;
244 private ToolItemAction mClearAction;
246 private ToolItemAction[] mLogLevelActions;
247 private String[] mLogLevelIcons = {
248 "v.png", //$NON-NLS-1S
249 "d.png", //$NON-NLS-1S
250 "i.png", //$NON-NLS-1S
251 "w.png", //$NON-NLS-1S
252 "e.png", //$NON-NLS-1S
255 protected Clipboard mClipboard;
257 private MenuItem mCopyMenuItem;
259 private MenuItem mSelectAllMenuItem;
261 private TableFocusListener mTableListener;
263 private DeviceExplorer mExplorer = null;
264 private Shell mExplorerShell = null;
266 private EmulatorControlPanel mEmulatorPanel;
268 private EventLogPanel mEventLogPanel;
270 private Image mTracingStartImage;
272 private Image mTracingStopImage;
274 private ImageLoader mDdmUiLibLoader;
276 private class TableFocusListener implements ITableFocusListener {
278 private IFocusedTableActivator mCurrentActivator;
280 public void focusGained(IFocusedTableActivator activator) {
281 mCurrentActivator = activator;
282 if (mCopyMenuItem.isDisposed() == false) {
283 mCopyMenuItem.setEnabled(true);
284 mSelectAllMenuItem.setEnabled(true);
288 public void focusLost(IFocusedTableActivator activator) {
289 // if we move from one table to another, it's unclear
290 // if the old table lose its focus before the new
291 // one gets the focus, so we need to check.
292 if (activator == mCurrentActivator) {
294 if (mCopyMenuItem.isDisposed() == false) {
295 mCopyMenuItem.setEnabled(false);
296 mSelectAllMenuItem.setEnabled(false);
301 public void copy(Clipboard clipboard) {
302 if (mCurrentActivator != null) {
303 mCurrentActivator.copy(clipboard);
307 public void selectAll() {
308 if (mCurrentActivator != null) {
309 mCurrentActivator.selectAll();
315 * Handler for HPROF dumps.
316 * This will always prompt the user to save the HPROF file.
318 private class HProfHandler extends BaseFileHandler implements IHprofDumpHandler {
320 public HProfHandler(Shell parentShell) {
324 public void onEndFailure(final Client client, final String message) {
325 mDisplay.asyncExec(new Runnable() {
328 displayErrorFromUiThread(
329 "Unable to create HPROF file for application '%1$s'\n\n%2$s" +
330 "Check logcat for more information.",
331 client.getClientData().getClientDescription(),
332 message != null ? message + "\n\n" : "");
334 // this will make sure the dump hprof button is re-enabled for the
335 // current selection. as the client is finished dumping an hprof file
342 public void onSuccess(final String remoteFilePath, final Client client) {
343 mDisplay.asyncExec(new Runnable() {
345 final IDevice device = client.getDevice();
347 // get the sync service to pull the HPROF file
348 final SyncService sync = client.getDevice().getSyncService();
351 client.getClientData().getClientDescription() + ".hprof",
352 remoteFilePath, "Save HPROF file");
354 displayErrorFromUiThread(
355 "Unable to download HPROF file from device '%1$s'.",
356 device.getSerialNumber());
358 } catch (SyncException e) {
359 if (e.wasCanceled() == false) {
360 displayErrorFromUiThread(
361 "Unable to download HPROF file from device '%1$s'.\n\n%2$s",
362 device.getSerialNumber(), e.getMessage());
364 } catch (Exception e) {
365 displayErrorFromUiThread("Unable to download HPROF file from device '%1$s'.",
366 device.getSerialNumber());
369 // this will make sure the dump hprof button is re-enabled for the
370 // current selection. as the client is finished dumping an hprof file
377 public void onSuccess(final byte[] data, final Client client) {
378 mDisplay.asyncExec(new Runnable() {
380 promptAndSave(client.getClientData().getClientDescription() + ".hprof", data,
387 protected String getDialogTitle() {
388 return "HPROF Error";
394 * Generic constructor.
397 mPanels[PANEL_INFO] = new InfoPanel();
398 mPanels[PANEL_THREAD] = new ThreadPanel();
399 mPanels[PANEL_HEAP] = new HeapPanel();
400 if (PrefsDialog.getStore().getBoolean(PrefsDialog.SHOW_NATIVE_HEAP)) {
401 mPanels[PANEL_NATIVE_HEAP] = new NativeHeapPanel();
403 mPanels[PANEL_NATIVE_HEAP] = null;
405 mPanels[PANEL_ALLOCATIONS] = new AllocationPanel();
406 mPanels[PANEL_SYSINFO] = new SysinfoPanel();
410 * Get singleton instance of the UI thread.
412 public static UIThread getInstance() {
417 * Return the Display. Don't try this unless you're in the UI thread.
419 public Display getDisplay() {
423 public void asyncExec(Runnable r) {
424 if (mDisplay != null && mDisplay.isDisposed() == false) {
425 mDisplay.asyncExec(r);
429 /** returns the IPreferenceStore */
430 public IPreferenceStore getStore() {
431 return PrefsDialog.getStore();
435 * Create SWT objects and drive the user interface event loop.
436 * @param ddmsParentLocation location of the folder that contains ddms.
438 public void runUI(String ddmsParentLocation) {
439 Display.setAppName(APP_NAME);
440 mDisplay = new Display();
441 final Shell shell = new Shell(mDisplay);
443 // create the image loaders for DDMS and DDMUILIB
444 mDdmUiLibLoader = ImageLoader.getDdmUiLibLoader();
446 shell.setImage(ImageLoader.getLoader(this.getClass()).loadImage(mDisplay,
447 "ddms-128.png", //$NON-NLS-1$
450 Log.setLogOutput(new ILogOutput() {
451 public void printAndPromptLog(final LogLevel logLevel, final String tag,
452 final String message) {
453 Log.printLog(logLevel, tag, message);
454 // dialog box only run in UI thread..
455 mDisplay.asyncExec(new Runnable() {
457 Shell activeShell = mDisplay.getActiveShell();
458 if (logLevel == LogLevel.ERROR) {
459 MessageDialog.openError(activeShell, tag, message);
461 MessageDialog.openWarning(activeShell, tag, message);
467 public void printLog(LogLevel logLevel, String tag, String message) {
468 Log.printLog(logLevel, tag, message);
472 // set the handler for hprof dump
473 ClientData.setHprofDumpHandler(new HProfHandler(shell));
474 ClientData.setMethodProfilingHandler(new MethodProfilingHandler(shell));
476 // [try to] ensure ADB is running
477 // in the new SDK, adb is in the platform-tools, but when run from the command line
478 // in the Android source tree, then adb is next to ddms.
480 if (ddmsParentLocation != null && ddmsParentLocation.length() != 0) {
481 // check if there's a platform-tools folder
482 File platformTools = new File(new File(ddmsParentLocation).getParent(),
483 "platform-tools"); //$NON-NLS-1$
484 if (platformTools.isDirectory()) {
485 adbLocation = platformTools.getAbsolutePath() + File.separator + "adb"; //$NON-NLS-1$
487 adbLocation = ddmsParentLocation + File.separator + "adb"; //$NON-NLS-1$
490 adbLocation = "adb"; //$NON-NLS-1$
493 AndroidDebugBridge.init(true /* debugger support */);
494 AndroidDebugBridge.createBridge(adbLocation, true /* forceNewBridge */);
496 // we need to listen to client change to be notified of client status (profiling) change
497 AndroidDebugBridge.addClientChangeListener(this);
499 shell.setText("Dalvik Debug Monitor");
500 setConfirmClose(shell);
502 createWidgets(shell);
505 setSizeAndPosition(shell);
508 Log.d("ddms", "UI is up");
510 while (!shell.isDisposed()) {
511 if (!mDisplay.readAndDispatch())
514 if (useOldLogCatView()) {
515 mLogPanel.stopLogCat(true);
518 mDevicePanel.dispose();
519 for (TablePanel panel : mPanels) {
525 ImageLoader.dispose();
528 Log.d("ddms", "UI is down");
532 * Set the size and position of the main window from the preference, and
533 * setup listeners for control events (resize/move of the window)
535 private void setSizeAndPosition(final Shell shell) {
536 shell.setMinimumSize(400, 200);
538 // get the x/y and w/h from the prefs
539 PreferenceStore prefs = PrefsDialog.getStore();
540 int x = prefs.getInt(PrefsDialog.SHELL_X);
541 int y = prefs.getInt(PrefsDialog.SHELL_Y);
542 int w = prefs.getInt(PrefsDialog.SHELL_WIDTH);
543 int h = prefs.getInt(PrefsDialog.SHELL_HEIGHT);
545 // check that we're not out of the display area
546 Rectangle rect = mDisplay.getClientArea();
547 // first check the width/height
548 if (w > rect.width) {
550 prefs.setValue(PrefsDialog.SHELL_WIDTH, rect.width);
552 if (h > rect.height) {
554 prefs.setValue(PrefsDialog.SHELL_HEIGHT, rect.height);
556 // then check x. Make sure the left corner is in the screen
559 prefs.setValue(PrefsDialog.SHELL_X, rect.x);
560 } else if (x >= rect.x + rect.width) {
561 x = rect.x + rect.width - w;
562 prefs.setValue(PrefsDialog.SHELL_X, rect.x);
564 // then check y. Make sure the left corner is in the screen
567 prefs.setValue(PrefsDialog.SHELL_Y, rect.y);
568 } else if (y >= rect.y + rect.height) {
569 y = rect.y + rect.height - h;
570 prefs.setValue(PrefsDialog.SHELL_Y, rect.y);
573 // now we can set the location/size
574 shell.setBounds(x, y, w, h);
576 // add listener for resize/move
577 shell.addControlListener(new ControlListener() {
578 public void controlMoved(ControlEvent e) {
580 Rectangle controlBounds = shell.getBounds();
581 // store in pref file
582 PreferenceStore currentPrefs = PrefsDialog.getStore();
583 currentPrefs.setValue(PrefsDialog.SHELL_X, controlBounds.x);
584 currentPrefs.setValue(PrefsDialog.SHELL_Y, controlBounds.y);
587 public void controlResized(ControlEvent e) {
589 Rectangle controlBounds = shell.getBounds();
590 // store in pref file
591 PreferenceStore currentPrefs = PrefsDialog.getStore();
592 currentPrefs.setValue(PrefsDialog.SHELL_WIDTH, controlBounds.width);
593 currentPrefs.setValue(PrefsDialog.SHELL_HEIGHT, controlBounds.height);
599 * Set the size and position of the file explorer window from the
600 * preference, and setup listeners for control events (resize/move of
603 private void setExplorerSizeAndPosition(final Shell shell) {
604 shell.setMinimumSize(400, 200);
606 // get the x/y and w/h from the prefs
607 PreferenceStore prefs = PrefsDialog.getStore();
608 int x = prefs.getInt(PrefsDialog.EXPLORER_SHELL_X);
609 int y = prefs.getInt(PrefsDialog.EXPLORER_SHELL_Y);
610 int w = prefs.getInt(PrefsDialog.EXPLORER_SHELL_WIDTH);
611 int h = prefs.getInt(PrefsDialog.EXPLORER_SHELL_HEIGHT);
613 // check that we're not out of the display area
614 Rectangle rect = mDisplay.getClientArea();
615 // first check the width/height
616 if (w > rect.width) {
618 prefs.setValue(PrefsDialog.EXPLORER_SHELL_WIDTH, rect.width);
620 if (h > rect.height) {
622 prefs.setValue(PrefsDialog.EXPLORER_SHELL_HEIGHT, rect.height);
624 // then check x. Make sure the left corner is in the screen
627 prefs.setValue(PrefsDialog.EXPLORER_SHELL_X, rect.x);
628 } else if (x >= rect.x + rect.width) {
629 x = rect.x + rect.width - w;
630 prefs.setValue(PrefsDialog.EXPLORER_SHELL_X, rect.x);
632 // then check y. Make sure the left corner is in the screen
635 prefs.setValue(PrefsDialog.EXPLORER_SHELL_Y, rect.y);
636 } else if (y >= rect.y + rect.height) {
637 y = rect.y + rect.height - h;
638 prefs.setValue(PrefsDialog.EXPLORER_SHELL_Y, rect.y);
641 // now we can set the location/size
642 shell.setBounds(x, y, w, h);
644 // add listener for resize/move
645 shell.addControlListener(new ControlListener() {
646 public void controlMoved(ControlEvent e) {
648 Rectangle controlBounds = shell.getBounds();
649 // store in pref file
650 PreferenceStore currentPrefs = PrefsDialog.getStore();
651 currentPrefs.setValue(PrefsDialog.EXPLORER_SHELL_X, controlBounds.x);
652 currentPrefs.setValue(PrefsDialog.EXPLORER_SHELL_Y, controlBounds.y);
655 public void controlResized(ControlEvent e) {
657 Rectangle controlBounds = shell.getBounds();
658 // store in pref file
659 PreferenceStore currentPrefs = PrefsDialog.getStore();
660 currentPrefs.setValue(PrefsDialog.EXPLORER_SHELL_WIDTH, controlBounds.width);
661 currentPrefs.setValue(PrefsDialog.EXPLORER_SHELL_HEIGHT, controlBounds.height);
667 * Set the confirm-before-close dialog.
669 private void setConfirmClose(final Shell shell) {
670 // Note: there was some commented out code to display a confirmation box
671 // when closing. The feature seems unnecessary and the code was not being
672 // used, so it has been removed.
676 * Create the menu bar and items.
678 private void createMenus(final Shell shell) {
680 Menu menuBar = new Menu(shell, SWT.BAR);
682 // create top-level items
683 MenuItem fileItem = new MenuItem(menuBar, SWT.CASCADE);
684 fileItem.setText("&File");
685 MenuItem editItem = new MenuItem(menuBar, SWT.CASCADE);
686 editItem.setText("&Edit");
687 MenuItem actionItem = new MenuItem(menuBar, SWT.CASCADE);
688 actionItem.setText("&Actions");
689 MenuItem deviceItem = new MenuItem(menuBar, SWT.CASCADE);
690 deviceItem.setText("&Device");
692 // create top-level menus
693 Menu fileMenu = new Menu(menuBar);
694 fileItem.setMenu(fileMenu);
695 Menu editMenu = new Menu(menuBar);
696 editItem.setMenu(editMenu);
697 Menu actionMenu = new Menu(menuBar);
698 actionItem.setMenu(actionMenu);
699 Menu deviceMenu = new Menu(menuBar);
700 deviceItem.setMenu(deviceMenu);
704 // create File menu items
705 item = new MenuItem(fileMenu, SWT.NONE);
706 item.setText("&Static Port Configuration...");
707 item.addSelectionListener(new SelectionAdapter() {
709 public void widgetSelected(SelectionEvent e) {
710 StaticPortConfigDialog dlg = new StaticPortConfigDialog(shell);
715 IMenuBarEnhancer enhancer = MenuBarEnhancer.setupMenu(APP_NAME, fileMenu,
716 new IMenuBarCallback() {
717 public void printError(String format, Object... args) {
718 Log.e("DDMS Menu Bar", String.format(format, args));
721 public void onPreferencesMenuSelected() {
722 PrefsDialog.run(shell);
725 public void onAboutMenuSelected() {
726 AboutDialog dlg = new AboutDialog(shell);
731 if (enhancer.getMenuBarMode() == MenuBarMode.GENERIC) {
732 new MenuItem(fileMenu, SWT.SEPARATOR);
734 item = new MenuItem(fileMenu, SWT.NONE);
735 item.setText("E&xit\tCtrl-Q");
736 item.setAccelerator('Q' | (Main.isMac() ? SWT.COMMAND : SWT.CONTROL));
737 item.addSelectionListener(new SelectionAdapter() {
739 public void widgetSelected(SelectionEvent e) {
745 // create edit menu items
746 mCopyMenuItem = new MenuItem(editMenu, SWT.NONE);
747 mCopyMenuItem.setText("&Copy\tCtrl-C");
748 mCopyMenuItem.setAccelerator('C' | SWT.COMMAND);
749 mCopyMenuItem.addSelectionListener(new SelectionAdapter() {
751 public void widgetSelected(SelectionEvent e) {
752 mTableListener.copy(mClipboard);
756 new MenuItem(editMenu, SWT.SEPARATOR);
758 mSelectAllMenuItem = new MenuItem(editMenu, SWT.NONE);
759 mSelectAllMenuItem.setText("Select &All\tCtrl-A");
760 mSelectAllMenuItem.setAccelerator('A' | SWT.COMMAND);
761 mSelectAllMenuItem.addSelectionListener(new SelectionAdapter() {
763 public void widgetSelected(SelectionEvent e) {
764 mTableListener.selectAll();
768 // create Action menu items
769 // TODO: this should come with a confirmation dialog
770 final MenuItem actionHaltItem = new MenuItem(actionMenu, SWT.NONE);
771 actionHaltItem.setText("&Halt VM");
772 actionHaltItem.addSelectionListener(new SelectionAdapter() {
774 public void widgetSelected(SelectionEvent e) {
775 mDevicePanel.killSelectedClient();
779 final MenuItem actionCauseGcItem = new MenuItem(actionMenu, SWT.NONE);
780 actionCauseGcItem.setText("Cause &GC");
781 actionCauseGcItem.addSelectionListener(new SelectionAdapter() {
783 public void widgetSelected(SelectionEvent e) {
784 mDevicePanel.forceGcOnSelectedClient();
788 final MenuItem actionResetAdb = new MenuItem(actionMenu, SWT.NONE);
789 actionResetAdb.setText("&Reset adb");
790 actionResetAdb.addSelectionListener(new SelectionAdapter() {
792 public void widgetSelected(SelectionEvent e) {
793 AndroidDebugBridge bridge = AndroidDebugBridge.getBridge();
794 if (bridge != null) {
800 // configure Action items based on current state
801 actionMenu.addMenuListener(new MenuAdapter() {
803 public void menuShown(MenuEvent e) {
804 actionHaltItem.setEnabled(mTBHalt.getEnabled() && mCurrentClient != null);
805 actionCauseGcItem.setEnabled(mTBCauseGc.getEnabled() && mCurrentClient != null);
806 actionResetAdb.setEnabled(true);
810 // create Device menu items
811 final MenuItem screenShotItem = new MenuItem(deviceMenu, SWT.NONE);
813 // The \tCtrl-S "keybinding text" here isn't right for the Mac - but
814 // it's stripped out and replaced by the proper keyboard accelerator
815 // text (e.g. the unicode symbol for the command key + S) anyway
816 // so it's fine to leave it there for the other platforms.
817 screenShotItem.setText("&Screen capture...\tCtrl-S");
818 screenShotItem.setAccelerator('S' | (Main.isMac() ? SWT.COMMAND : SWT.CONTROL));
819 screenShotItem.addSelectionListener(new SelectionAdapter() {
821 public void widgetSelected(SelectionEvent e) {
822 if (mCurrentDevice != null) {
823 ScreenShotDialog dlg = new ScreenShotDialog(shell);
824 dlg.open(mCurrentDevice);
829 new MenuItem(deviceMenu, SWT.SEPARATOR);
831 final MenuItem explorerItem = new MenuItem(deviceMenu, SWT.NONE);
832 explorerItem.setText("File Explorer...");
833 explorerItem.addSelectionListener(new SelectionAdapter() {
835 public void widgetSelected(SelectionEvent e) {
836 createFileExplorer();
840 new MenuItem(deviceMenu, SWT.SEPARATOR);
842 final MenuItem processItem = new MenuItem(deviceMenu, SWT.NONE);
843 processItem.setText("Show &process status...");
844 processItem.addSelectionListener(new SelectionAdapter() {
846 public void widgetSelected(SelectionEvent e) {
847 DeviceCommandDialog dlg;
848 dlg = new DeviceCommandDialog("ps -x", "ps-x.txt", shell);
849 dlg.open(mCurrentDevice);
853 final MenuItem deviceStateItem = new MenuItem(deviceMenu, SWT.NONE);
854 deviceStateItem.setText("Dump &device state...");
855 deviceStateItem.addSelectionListener(new SelectionAdapter() {
857 public void widgetSelected(SelectionEvent e) {
858 DeviceCommandDialog dlg;
859 dlg = new DeviceCommandDialog("/system/bin/dumpstate /proc/self/fd/0",
860 "device-state.txt", shell);
861 dlg.open(mCurrentDevice);
865 final MenuItem appStateItem = new MenuItem(deviceMenu, SWT.NONE);
866 appStateItem.setText("Dump &app state...");
867 appStateItem.setEnabled(false);
868 appStateItem.addSelectionListener(new SelectionAdapter() {
870 public void widgetSelected(SelectionEvent e) {
871 DeviceCommandDialog dlg;
872 dlg = new DeviceCommandDialog("dumpsys", "app-state.txt", shell);
873 dlg.open(mCurrentDevice);
877 final MenuItem radioStateItem = new MenuItem(deviceMenu, SWT.NONE);
878 radioStateItem.setText("Dump &radio state...");
879 radioStateItem.addSelectionListener(new SelectionAdapter() {
881 public void widgetSelected(SelectionEvent e) {
882 DeviceCommandDialog dlg;
883 dlg = new DeviceCommandDialog(
884 "cat /data/logs/radio.4 /data/logs/radio.3"
885 + " /data/logs/radio.2 /data/logs/radio.1"
886 + " /data/logs/radio",
887 "radio-state.txt", shell);
888 dlg.open(mCurrentDevice);
892 final MenuItem logCatItem = new MenuItem(deviceMenu, SWT.NONE);
893 logCatItem.setText("Run &logcat...");
894 logCatItem.addSelectionListener(new SelectionAdapter() {
896 public void widgetSelected(SelectionEvent e) {
897 DeviceCommandDialog dlg;
898 dlg = new DeviceCommandDialog("logcat '*:d jdwp:w'", "log.txt",
900 dlg.open(mCurrentDevice);
904 // configure Action items based on current state
905 deviceMenu.addMenuListener(new MenuAdapter() {
907 public void menuShown(MenuEvent e) {
908 boolean deviceEnabled = mCurrentDevice != null;
909 screenShotItem.setEnabled(deviceEnabled);
910 explorerItem.setEnabled(deviceEnabled);
911 processItem.setEnabled(deviceEnabled);
912 deviceStateItem.setEnabled(deviceEnabled);
913 appStateItem.setEnabled(deviceEnabled);
914 radioStateItem.setEnabled(deviceEnabled);
915 logCatItem.setEnabled(deviceEnabled);
919 // tell the shell to use this menu
920 shell.setMenuBar(menuBar);
924 * Create the widgets in the main application window. The basic layout is a
925 * two-panel sash, with a scrolling list of VMs on the left and detailed
926 * output for a single VM on the right.
928 private void createWidgets(final Shell shell) {
929 Color darkGray = shell.getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY);
932 * Create three areas: tool bar, split panels, status line
934 shell.setLayout(new GridLayout(1, false));
937 final Composite panelArea = new Composite(shell, SWT.BORDER);
939 // make the panel area absorb all space
940 panelArea.setLayoutData(new GridData(GridData.FILL_BOTH));
943 mStatusLine = new Label(shell, SWT.NONE);
945 // make status line extend all the way across
946 mStatusLine.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
948 mStatusLine.setText("Initializing...");
951 * Configure the split-panel area.
953 final PreferenceStore prefs = PrefsDialog.getStore();
955 Composite topPanel = new Composite(panelArea, SWT.NONE);
956 final Sash sash = new Sash(panelArea, SWT.HORIZONTAL);
957 sash.setBackground(darkGray);
958 Composite bottomPanel = new Composite(panelArea, SWT.NONE);
960 panelArea.setLayout(new FormLayout());
962 createTopPanel(topPanel, darkGray);
964 if (useOldLogCatView()) {
965 createBottomPanel(bottomPanel);
967 createLogCatView(bottomPanel);
971 FormData data = new FormData();
972 data.top = new FormAttachment(0, 0);
973 data.bottom = new FormAttachment(sash, 0);
974 data.left = new FormAttachment(0, 0);
975 data.right = new FormAttachment(100, 0);
976 topPanel.setLayoutData(data);
978 final FormData sashData = new FormData();
979 if (prefs != null && prefs.contains(PREFERENCE_LOGSASH)) {
980 sashData.top = new FormAttachment(0, prefs.getInt(
981 PREFERENCE_LOGSASH));
983 sashData.top = new FormAttachment(50,0); // 50% across
985 sashData.left = new FormAttachment(0, 0);
986 sashData.right = new FormAttachment(100, 0);
987 sash.setLayoutData(sashData);
989 data = new FormData();
990 data.top = new FormAttachment(sash, 0);
991 data.bottom = new FormAttachment(100, 0);
992 data.left = new FormAttachment(0, 0);
993 data.right = new FormAttachment(100, 0);
994 bottomPanel.setLayoutData(data);
996 // allow resizes, but cap at minPanelWidth
997 sash.addListener(SWT.Selection, new Listener() {
998 public void handleEvent(Event e) {
999 Rectangle sashRect = sash.getBounds();
1000 Rectangle panelRect = panelArea.getClientArea();
1001 int bottom = panelRect.height - sashRect.height - 100;
1002 e.y = Math.max(Math.min(e.y, bottom), 100);
1003 if (e.y != sashRect.y) {
1004 sashData.top = new FormAttachment(0, e.y);
1005 if (prefs != null) {
1006 prefs.setValue(PREFERENCE_LOGSASH, e.y);
1013 // add a global focus listener for all the tables
1014 mTableListener = new TableFocusListener();
1016 // now set up the listener in the various panels
1017 if (useOldLogCatView()) {
1018 mLogPanel.setTableFocusListener(mTableListener);
1020 mEventLogPanel.setTableFocusListener(mTableListener);
1021 for (TablePanel p : mPanels) {
1023 p.setTableFocusListener(mTableListener);
1027 mStatusLine.setText("");
1031 * Populate the tool bar.
1033 private void createDevicePanelToolBar(ToolBar toolBar) {
1034 Display display = toolBar.getDisplay();
1036 // add "show heap updates" button
1037 mTBShowHeapUpdates = new ToolItem(toolBar, SWT.CHECK);
1038 mTBShowHeapUpdates.setImage(mDdmUiLibLoader.loadImage(display,
1039 DevicePanel.ICON_HEAP, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1040 mTBShowHeapUpdates.setToolTipText("Show heap updates");
1041 mTBShowHeapUpdates.setEnabled(false);
1042 mTBShowHeapUpdates.addSelectionListener(new SelectionAdapter() {
1044 public void widgetSelected(SelectionEvent e) {
1045 if (mCurrentClient != null) {
1046 // boolean status = ((ToolItem)e.item).getSelection();
1047 // invert previous state
1048 boolean enable = !mCurrentClient.isHeapUpdateEnabled();
1049 mCurrentClient.setHeapUpdateEnabled(enable);
1051 e.doit = false; // this has no effect?
1056 // add "dump HPROF" button
1057 mTBDumpHprof = new ToolItem(toolBar, SWT.PUSH);
1058 mTBDumpHprof.setToolTipText("Dump HPROF file");
1059 mTBDumpHprof.setEnabled(false);
1060 mTBDumpHprof.setImage(mDdmUiLibLoader.loadImage(display,
1061 DevicePanel.ICON_HPROF, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1062 mTBDumpHprof.addSelectionListener(new SelectionAdapter() {
1064 public void widgetSelected(SelectionEvent e) {
1065 mDevicePanel.dumpHprof();
1067 // this will make sure the dump hprof button is disabled for the current selection
1068 // as the client is already dumping an hprof file
1073 // add "cause GC" button
1074 mTBCauseGc = new ToolItem(toolBar, SWT.PUSH);
1075 mTBCauseGc.setToolTipText("Cause an immediate GC");
1076 mTBCauseGc.setEnabled(false);
1077 mTBCauseGc.setImage(mDdmUiLibLoader.loadImage(display,
1078 DevicePanel.ICON_GC, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1079 mTBCauseGc.addSelectionListener(new SelectionAdapter() {
1081 public void widgetSelected(SelectionEvent e) {
1082 mDevicePanel.forceGcOnSelectedClient();
1086 new ToolItem(toolBar, SWT.SEPARATOR);
1088 // add "show thread updates" button
1089 mTBShowThreadUpdates = new ToolItem(toolBar, SWT.CHECK);
1090 mTBShowThreadUpdates.setImage(mDdmUiLibLoader.loadImage(display,
1091 DevicePanel.ICON_THREAD, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1092 mTBShowThreadUpdates.setToolTipText("Show thread updates");
1093 mTBShowThreadUpdates.setEnabled(false);
1094 mTBShowThreadUpdates.addSelectionListener(new SelectionAdapter() {
1096 public void widgetSelected(SelectionEvent e) {
1097 if (mCurrentClient != null) {
1098 // boolean status = ((ToolItem)e.item).getSelection();
1099 // invert previous state
1100 boolean enable = !mCurrentClient.isThreadUpdateEnabled();
1102 mCurrentClient.setThreadUpdateEnabled(enable);
1104 e.doit = false; // this has no effect?
1109 // add a start/stop method tracing
1110 mTracingStartImage = mDdmUiLibLoader.loadImage(display,
1111 DevicePanel.ICON_TRACING_START,
1112 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null);
1113 mTracingStopImage = mDdmUiLibLoader.loadImage(display,
1114 DevicePanel.ICON_TRACING_STOP,
1115 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null);
1116 mTBProfiling = new ToolItem(toolBar, SWT.PUSH);
1117 mTBProfiling.setToolTipText("Start Method Profiling");
1118 mTBProfiling.setEnabled(false);
1119 mTBProfiling.setImage(mTracingStartImage);
1120 mTBProfiling.addSelectionListener(new SelectionAdapter() {
1122 public void widgetSelected(SelectionEvent e) {
1123 mDevicePanel.toggleMethodProfiling();
1127 new ToolItem(toolBar, SWT.SEPARATOR);
1129 // add "kill VM" button; need to make this visually distinct from
1130 // the status update buttons
1131 mTBHalt = new ToolItem(toolBar, SWT.PUSH);
1132 mTBHalt.setToolTipText("Halt the target VM");
1133 mTBHalt.setEnabled(false);
1134 mTBHalt.setImage(mDdmUiLibLoader.loadImage(display,
1135 DevicePanel.ICON_HALT, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1136 mTBHalt.addSelectionListener(new SelectionAdapter() {
1138 public void widgetSelected(SelectionEvent e) {
1139 mDevicePanel.killSelectedClient();
1146 private void createTopPanel(final Composite comp, Color darkGray) {
1147 final PreferenceStore prefs = PrefsDialog.getStore();
1149 comp.setLayout(new FormLayout());
1151 Composite leftPanel = new Composite(comp, SWT.NONE);
1152 final Sash sash = new Sash(comp, SWT.VERTICAL);
1153 sash.setBackground(darkGray);
1154 Composite rightPanel = new Composite(comp, SWT.NONE);
1156 createLeftPanel(leftPanel);
1157 createRightPanel(rightPanel);
1159 FormData data = new FormData();
1160 data.top = new FormAttachment(0, 0);
1161 data.bottom = new FormAttachment(100, 0);
1162 data.left = new FormAttachment(0, 0);
1163 data.right = new FormAttachment(sash, 0);
1164 leftPanel.setLayoutData(data);
1166 final FormData sashData = new FormData();
1167 sashData.top = new FormAttachment(0, 0);
1168 sashData.bottom = new FormAttachment(100, 0);
1169 if (prefs != null && prefs.contains(PREFERENCE_SASH)) {
1170 sashData.left = new FormAttachment(0, prefs.getInt(
1173 // position the sash 380 from the right instead of x% (done by using
1174 // FormAttachment(x, 0)) in order to keep the sash at the same
1176 // from the left when the window is resized.
1177 // 380px is just enough to display the left table with no horizontal
1178 // scrollbar with the default font.
1179 sashData.left = new FormAttachment(0, 380);
1181 sash.setLayoutData(sashData);
1183 data = new FormData();
1184 data.top = new FormAttachment(0, 0);
1185 data.bottom = new FormAttachment(100, 0);
1186 data.left = new FormAttachment(sash, 0);
1187 data.right = new FormAttachment(100, 0);
1188 rightPanel.setLayoutData(data);
1190 final int minPanelWidth = 60;
1192 // allow resizes, but cap at minPanelWidth
1193 sash.addListener(SWT.Selection, new Listener() {
1194 public void handleEvent(Event e) {
1195 Rectangle sashRect = sash.getBounds();
1196 Rectangle panelRect = comp.getClientArea();
1197 int right = panelRect.width - sashRect.width - minPanelWidth;
1198 e.x = Math.max(Math.min(e.x, right), minPanelWidth);
1199 if (e.x != sashRect.x) {
1200 sashData.left = new FormAttachment(0, e.x);
1201 if (prefs != null) {
1202 prefs.setValue(PREFERENCE_SASH, e.x);
1210 private void createBottomPanel(final Composite comp) {
1211 final PreferenceStore prefs = PrefsDialog.getStore();
1214 Display display = comp.getDisplay();
1215 mClipboard = new Clipboard(display);
1217 LogColors colors = new LogColors();
1219 colors.infoColor = new Color(display, 0, 127, 0);
1220 colors.debugColor = new Color(display, 0, 0, 127);
1221 colors.errorColor = new Color(display, 255, 0, 0);
1222 colors.warningColor = new Color(display, 255, 127, 0);
1223 colors.verboseColor = new Color(display, 0, 0, 0);
1225 // set the preferences names
1226 LogPanel.PREFS_TIME = PREFS_COL_TIME;
1227 LogPanel.PREFS_LEVEL = PREFS_COL_LEVEL;
1228 LogPanel.PREFS_PID = PREFS_COL_PID;
1229 LogPanel.PREFS_TAG = PREFS_COL_TAG;
1230 LogPanel.PREFS_MESSAGE = PREFS_COL_MESSAGE;
1232 comp.setLayout(new GridLayout(1, false));
1234 ToolBar toolBar = new ToolBar(comp, SWT.HORIZONTAL);
1236 mCreateFilterAction = new ToolItemAction(toolBar, SWT.PUSH);
1237 mCreateFilterAction.item.setToolTipText("Create Filter");
1238 mCreateFilterAction.item.setImage(mDdmUiLibLoader.loadImage(mDisplay,
1239 "add.png", //$NON-NLS-1$
1240 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1241 mCreateFilterAction.item.addSelectionListener(new SelectionAdapter() {
1243 public void widgetSelected(SelectionEvent e) {
1244 mLogPanel.addFilter();
1248 mEditFilterAction = new ToolItemAction(toolBar, SWT.PUSH);
1249 mEditFilterAction.item.setToolTipText("Edit Filter");
1250 mEditFilterAction.item.setImage(mDdmUiLibLoader.loadImage(mDisplay,
1251 "edit.png", //$NON-NLS-1$
1252 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1253 mEditFilterAction.item.addSelectionListener(new SelectionAdapter() {
1255 public void widgetSelected(SelectionEvent e) {
1256 mLogPanel.editFilter();
1260 mDeleteFilterAction = new ToolItemAction(toolBar, SWT.PUSH);
1261 mDeleteFilterAction.item.setToolTipText("Delete Filter");
1262 mDeleteFilterAction.item.setImage(mDdmUiLibLoader.loadImage(mDisplay,
1263 "delete.png", //$NON-NLS-1$
1264 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1265 mDeleteFilterAction.item.addSelectionListener(new SelectionAdapter() {
1267 public void widgetSelected(SelectionEvent e) {
1268 mLogPanel.deleteFilter();
1273 new ToolItem(toolBar, SWT.SEPARATOR);
1275 LogLevel[] levels = LogLevel.values();
1276 mLogLevelActions = new ToolItemAction[mLogLevelIcons.length];
1277 for (int i = 0 ; i < mLogLevelActions.length; i++) {
1278 String name = levels[i].getStringValue();
1279 final ToolItemAction newAction = new ToolItemAction(toolBar, SWT.CHECK);
1280 mLogLevelActions[i] = newAction;
1281 //newAction.item.setText(name);
1282 newAction.item.addSelectionListener(new SelectionAdapter() {
1284 public void widgetSelected(SelectionEvent e) {
1285 // disable the other actions and record current index
1286 for (int k = 0 ; k < mLogLevelActions.length; k++) {
1287 ToolItemAction a = mLogLevelActions[k];
1288 if (a == newAction) {
1291 // set the log level
1292 mLogPanel.setCurrentFilterLogLevel(k+2);
1294 a.setChecked(false);
1300 newAction.item.setToolTipText(name);
1301 newAction.item.setImage(mDdmUiLibLoader.loadImage(mDisplay,
1303 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1306 new ToolItem(toolBar, SWT.SEPARATOR);
1308 mClearAction = new ToolItemAction(toolBar, SWT.PUSH);
1309 mClearAction.item.setToolTipText("Clear Log");
1311 mClearAction.item.setImage(mDdmUiLibLoader.loadImage(mDisplay,
1312 "clear.png", //$NON-NLS-1$
1313 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1314 mClearAction.item.addSelectionListener(new SelectionAdapter() {
1316 public void widgetSelected(SelectionEvent e) {
1321 new ToolItem(toolBar, SWT.SEPARATOR);
1323 mExportAction = new ToolItemAction(toolBar, SWT.PUSH);
1324 mExportAction.item.setToolTipText("Export Selection As Text...");
1325 mExportAction.item.setImage(mDdmUiLibLoader.loadImage(mDisplay,
1326 "save.png", //$NON-NLS-1$
1327 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1328 mExportAction.item.addSelectionListener(new SelectionAdapter() {
1330 public void widgetSelected(SelectionEvent e) {
1338 // now create the log view
1339 mLogPanel = new LogPanel(colors, new FilterStorage(), LogPanel.FILTER_MANUAL);
1341 mLogPanel.setActions(mDeleteFilterAction, mEditFilterAction, mLogLevelActions);
1343 String colMode = prefs.getString(PrefsDialog.LOGCAT_COLUMN_MODE);
1344 if (PrefsDialog.LOGCAT_COLUMN_MODE_AUTO.equals(colMode)) {
1345 mLogPanel.setColumnMode(LogPanel.COLUMN_MODE_AUTO);
1348 String fontStr = PrefsDialog.getStore().getString(PrefsDialog.LOGCAT_FONT);
1349 if (fontStr != null) {
1351 FontData fdat = new FontData(fontStr);
1352 mLogPanel.setFont(new Font(display, fdat));
1353 } catch (IllegalArgumentException e) {
1354 // Looks like fontStr isn't a valid font representation.
1355 // We do nothing in this case, the logcat view will use the default font.
1356 } catch (SWTError e2) {
1357 // Looks like the Font() constructor failed.
1358 // We do nothing in this case, the logcat view will use the default font.
1362 mLogPanel.createPanel(comp);
1364 // and start the logcat
1365 mLogPanel.startLogCat(mCurrentDevice);
1368 private void createLogCatView(Composite parent) {
1369 mLogCatPanel = new LogCatPanel(new LogCatReceiver(), DdmUiPreferences.getStore());
1370 mLogCatPanel.createPanel(parent);
1372 if (mCurrentDevice != null) {
1373 mLogCatPanel.deviceSelected(mCurrentDevice);
1378 * Create the contents of the left panel: a table of VMs.
1380 private void createLeftPanel(final Composite comp) {
1381 comp.setLayout(new GridLayout(1, false));
1382 ToolBar toolBar = new ToolBar(comp, SWT.HORIZONTAL | SWT.RIGHT | SWT.WRAP);
1383 toolBar.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
1384 createDevicePanelToolBar(toolBar);
1386 Composite c = new Composite(comp, SWT.NONE);
1387 c.setLayoutData(new GridData(GridData.FILL_BOTH));
1389 mDevicePanel = new DevicePanel(true /* showPorts */);
1390 mDevicePanel.createPanel(c);
1392 // add ourselves to the device panel selection listener
1393 mDevicePanel.addSelectionListener(this);
1397 * Create the contents of the right panel: tabs with VM information.
1399 private void createRightPanel(final Composite comp) {
1401 TabFolder tabFolder;
1403 comp.setLayout(new FillLayout());
1405 tabFolder = new TabFolder(comp, SWT.NONE);
1407 for (int i = 0; i < mPanels.length; i++) {
1408 if (mPanels[i] != null) {
1409 item = new TabItem(tabFolder, SWT.NONE);
1410 item.setText(mPanelNames[i]);
1411 item.setToolTipText(mPanelTips[i]);
1412 item.setControl(mPanels[i].createPanel(tabFolder));
1416 // add the emulator control panel to the folders.
1417 item = new TabItem(tabFolder, SWT.NONE);
1418 item.setText("Emulator Control");
1419 item.setToolTipText("Emulator Control Panel");
1420 mEmulatorPanel = new EmulatorControlPanel();
1421 item.setControl(mEmulatorPanel.createPanel(tabFolder));
1423 // add the event log panel to the folders.
1424 item = new TabItem(tabFolder, SWT.NONE);
1425 item.setText("Event Log");
1426 item.setToolTipText("Event Log");
1428 // create the composite that will hold the toolbar and the event log panel.
1429 Composite eventLogTopComposite = new Composite(tabFolder, SWT.NONE);
1430 item.setControl(eventLogTopComposite);
1431 eventLogTopComposite.setLayout(new GridLayout(1, false));
1433 // create the toolbar and the actions
1434 ToolBar toolbar = new ToolBar(eventLogTopComposite, SWT.HORIZONTAL);
1435 toolbar.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
1437 ToolItemAction optionsAction = new ToolItemAction(toolbar, SWT.PUSH);
1438 optionsAction.item.setToolTipText("Opens the options panel");
1439 optionsAction.item.setImage(mDdmUiLibLoader.loadImage(comp.getDisplay(),
1440 "edit.png", //$NON-NLS-1$
1441 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1443 ToolItemAction clearAction = new ToolItemAction(toolbar, SWT.PUSH);
1444 clearAction.item.setToolTipText("Clears the event log");
1445 clearAction.item.setImage(mDdmUiLibLoader.loadImage(comp.getDisplay(),
1446 "clear.png", //$NON-NLS-1$
1447 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1449 new ToolItem(toolbar, SWT.SEPARATOR);
1451 ToolItemAction saveAction = new ToolItemAction(toolbar, SWT.PUSH);
1452 saveAction.item.setToolTipText("Saves the event log");
1453 saveAction.item.setImage(mDdmUiLibLoader.loadImage(comp.getDisplay(),
1454 "save.png", //$NON-NLS-1$
1455 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1457 ToolItemAction loadAction = new ToolItemAction(toolbar, SWT.PUSH);
1458 loadAction.item.setToolTipText("Loads an event log");
1459 loadAction.item.setImage(mDdmUiLibLoader.loadImage(comp.getDisplay(),
1460 "load.png", //$NON-NLS-1$
1461 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1463 ToolItemAction importBugAction = new ToolItemAction(toolbar, SWT.PUSH);
1464 importBugAction.item.setToolTipText("Imports a bug report");
1465 importBugAction.item.setImage(mDdmUiLibLoader.loadImage(comp.getDisplay(),
1466 "importBug.png", //$NON-NLS-1$
1467 DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
1469 // create the event log panel
1470 mEventLogPanel = new EventLogPanel();
1472 // set the external actions
1473 mEventLogPanel.setActions(optionsAction, clearAction, saveAction, loadAction,
1477 mEventLogPanel.createPanel(eventLogTopComposite);
1480 private void createFileExplorer() {
1481 if (mExplorer == null) {
1482 mExplorerShell = new Shell(mDisplay);
1485 mExplorerShell.setLayout(new GridLayout(1, false));
1488 ToolBar toolBar = new ToolBar(mExplorerShell, SWT.HORIZONTAL);
1489 toolBar.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
1491 ToolItemAction pullAction = new ToolItemAction(toolBar, SWT.PUSH);
1492 pullAction.item.setToolTipText("Pull File from Device");
1493 Image image = mDdmUiLibLoader.loadImage("pull.png", mDisplay); //$NON-NLS-1$
1494 if (image != null) {
1495 pullAction.item.setImage(image);
1497 // this is for debugging purpose when the icon is missing
1498 pullAction.item.setText("Pull"); //$NON-NLS-1$
1501 ToolItemAction pushAction = new ToolItemAction(toolBar, SWT.PUSH);
1502 pushAction.item.setToolTipText("Push file onto Device");
1503 image = mDdmUiLibLoader.loadImage("push.png", mDisplay); //$NON-NLS-1$
1504 if (image != null) {
1505 pushAction.item.setImage(image);
1507 // this is for debugging purpose when the icon is missing
1508 pushAction.item.setText("Push"); //$NON-NLS-1$
1511 ToolItemAction deleteAction = new ToolItemAction(toolBar, SWT.PUSH);
1512 deleteAction.item.setToolTipText("Delete");
1513 image = mDdmUiLibLoader.loadImage("delete.png", mDisplay); //$NON-NLS-1$
1514 if (image != null) {
1515 deleteAction.item.setImage(image);
1517 // this is for debugging purpose when the icon is missing
1518 deleteAction.item.setText("Delete"); //$NON-NLS-1$
1521 ToolItemAction createNewFolderAction = new ToolItemAction(toolBar, SWT.PUSH);
1522 createNewFolderAction.item.setToolTipText("New Folder");
1523 image = mDdmUiLibLoader.loadImage("add.png", mDisplay); //$NON-NLS-1$
1524 if (image != null) {
1525 createNewFolderAction.item.setImage(image);
1527 // this is for debugging purpose when the icon is missing
1528 createNewFolderAction.item.setText("New Folder"); //$NON-NLS-1$
1532 mExplorer = new DeviceExplorer();
1533 mExplorer.setActions(pushAction, pullAction, deleteAction, createNewFolderAction);
1535 pullAction.item.addSelectionListener(new SelectionAdapter() {
1537 public void widgetSelected(SelectionEvent e) {
1538 mExplorer.pullSelection();
1541 pullAction.setEnabled(false);
1543 pushAction.item.addSelectionListener(new SelectionAdapter() {
1545 public void widgetSelected(SelectionEvent e) {
1546 mExplorer.pushIntoSelection();
1549 pushAction.setEnabled(false);
1551 deleteAction.item.addSelectionListener(new SelectionAdapter() {
1553 public void widgetSelected(SelectionEvent e) {
1554 mExplorer.deleteSelection();
1557 deleteAction.setEnabled(false);
1559 createNewFolderAction.item.addSelectionListener(new SelectionAdapter() {
1561 public void widgetSelected(SelectionEvent e) {
1562 mExplorer.createNewFolderInSelection();
1565 createNewFolderAction.setEnabled(false);
1567 Composite parent = new Composite(mExplorerShell, SWT.NONE);
1568 parent.setLayoutData(new GridData(GridData.FILL_BOTH));
1570 mExplorer.createPanel(parent);
1571 mExplorer.switchDevice(mCurrentDevice);
1573 mExplorerShell.addShellListener(new ShellListener() {
1574 public void shellActivated(ShellEvent e) {
1578 public void shellClosed(ShellEvent e) {
1580 mExplorerShell = null;
1583 public void shellDeactivated(ShellEvent e) {
1587 public void shellDeiconified(ShellEvent e) {
1591 public void shellIconified(ShellEvent e) {
1596 mExplorerShell.pack();
1597 setExplorerSizeAndPosition(mExplorerShell);
1598 mExplorerShell.open();
1600 if (mExplorerShell != null) {
1601 mExplorerShell.forceActive();
1607 * Set the status line. TODO: make this a stack, so we can safely have
1608 * multiple things trying to set it all at once. Also specify an expiration?
1610 public void setStatusLine(final String str) {
1612 mDisplay.asyncExec(new Runnable() {
1614 doSetStatusLine(str);
1617 } catch (SWTException swte) {
1618 if (!mDisplay.isDisposed())
1623 private void doSetStatusLine(String str) {
1624 if (mStatusLine.isDisposed())
1627 if (!mStatusLine.getText().equals(str)) {
1628 mStatusLine.setText(str);
1630 // try { Thread.sleep(100); }
1631 // catch (InterruptedException ie) {}
1635 public void displayError(final String msg) {
1637 mDisplay.syncExec(new Runnable() {
1639 MessageDialog.openError(mDisplay.getActiveShell(), "Error",
1643 } catch (SWTException swte) {
1644 if (!mDisplay.isDisposed())
1649 private void enableButtons() {
1650 if (mCurrentClient != null) {
1651 mTBShowThreadUpdates.setSelection(mCurrentClient.isThreadUpdateEnabled());
1652 mTBShowThreadUpdates.setEnabled(true);
1653 mTBShowHeapUpdates.setSelection(mCurrentClient.isHeapUpdateEnabled());
1654 mTBShowHeapUpdates.setEnabled(true);
1655 mTBHalt.setEnabled(true);
1656 mTBCauseGc.setEnabled(true);
1658 ClientData data = mCurrentClient.getClientData();
1660 if (data.hasFeature(ClientData.FEATURE_HPROF)) {
1661 mTBDumpHprof.setEnabled(data.hasPendingHprofDump() == false);
1662 mTBDumpHprof.setToolTipText("Dump HPROF file");
1664 mTBDumpHprof.setEnabled(false);
1665 mTBDumpHprof.setToolTipText("Dump HPROF file (not supported by this VM)");
1668 if (data.hasFeature(ClientData.FEATURE_PROFILING)) {
1669 mTBProfiling.setEnabled(true);
1670 if (data.getMethodProfilingStatus() == MethodProfilingStatus.ON) {
1671 mTBProfiling.setToolTipText("Stop Method Profiling");
1672 mTBProfiling.setImage(mTracingStopImage);
1674 mTBProfiling.setToolTipText("Start Method Profiling");
1675 mTBProfiling.setImage(mTracingStartImage);
1678 mTBProfiling.setEnabled(false);
1679 mTBProfiling.setImage(mTracingStartImage);
1680 mTBProfiling.setToolTipText("Start Method Profiling (not supported by this VM)");
1683 // list is empty, disable these
1684 mTBShowThreadUpdates.setSelection(false);
1685 mTBShowThreadUpdates.setEnabled(false);
1686 mTBShowHeapUpdates.setSelection(false);
1687 mTBShowHeapUpdates.setEnabled(false);
1688 mTBHalt.setEnabled(false);
1689 mTBCauseGc.setEnabled(false);
1691 mTBDumpHprof.setEnabled(false);
1692 mTBDumpHprof.setToolTipText("Dump HPROF file");
1694 mTBProfiling.setEnabled(false);
1695 mTBProfiling.setImage(mTracingStartImage);
1696 mTBProfiling.setToolTipText("Start Method Profiling");
1701 * Sent when a new {@link IDevice} and {@link Client} are selected.
1702 * @param selectedDevice the selected device. If null, no devices are selected.
1703 * @param selectedClient The selected client. If null, no clients are selected.
1705 * @see IUiSelectionListener
1707 public void selectionChanged(IDevice selectedDevice, Client selectedClient) {
1708 if (mCurrentDevice != selectedDevice) {
1709 mCurrentDevice = selectedDevice;
1710 for (TablePanel panel : mPanels) {
1711 if (panel != null) {
1712 panel.deviceSelected(mCurrentDevice);
1716 mEmulatorPanel.deviceSelected(mCurrentDevice);
1717 if (useOldLogCatView()) {
1718 mLogPanel.deviceSelected(mCurrentDevice);
1720 mLogCatPanel.deviceSelected(mCurrentDevice);
1722 if (mEventLogPanel != null) {
1723 mEventLogPanel.deviceSelected(mCurrentDevice);
1726 if (mExplorer != null) {
1727 mExplorer.switchDevice(mCurrentDevice);
1731 if (mCurrentClient != selectedClient) {
1732 AndroidDebugBridge.getBridge().setSelectedClient(selectedClient);
1733 mCurrentClient = selectedClient;
1734 for (TablePanel panel : mPanels) {
1735 if (panel != null) {
1736 panel.clientSelected(mCurrentClient);
1744 public void clientChanged(Client client, int changeMask) {
1745 if ((changeMask & Client.CHANGE_METHOD_PROFILING_STATUS) ==
1746 Client.CHANGE_METHOD_PROFILING_STATUS) {
1747 if (mCurrentClient == client) {
1748 mDisplay.asyncExec(new Runnable() {
1750 // force refresh of the button enabled state.