blob: c98b3f1442d0fa6fdfa811ae353f88a7b8c4e97d [file] [log] [blame]
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.ddms;
import com.android.ddmlib.AndroidDebugBridge;
import com.android.ddmlib.Client;
import com.android.ddmlib.ClientData;
import com.android.ddmlib.IDevice;
import com.android.ddmlib.Log;
import com.android.ddmlib.SyncService;
import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener;
import com.android.ddmlib.ClientData.IHprofDumpHandler;
import com.android.ddmlib.ClientData.MethodProfilingStatus;
import com.android.ddmlib.Log.ILogOutput;
import com.android.ddmlib.Log.LogLevel;
import com.android.ddmlib.SyncService.SyncResult;
import com.android.ddmuilib.AllocationPanel;
import com.android.ddmuilib.DevicePanel;
import com.android.ddmuilib.EmulatorControlPanel;
import com.android.ddmuilib.HeapPanel;
import com.android.ddmuilib.ITableFocusListener;
import com.android.ddmuilib.ImageHelper;
import com.android.ddmuilib.ImageLoader;
import com.android.ddmuilib.InfoPanel;
import com.android.ddmuilib.NativeHeapPanel;
import com.android.ddmuilib.ScreenShotDialog;
import com.android.ddmuilib.SysinfoPanel;
import com.android.ddmuilib.TablePanel;
import com.android.ddmuilib.ThreadPanel;
import com.android.ddmuilib.DevicePanel.IUiSelectionListener;
import com.android.ddmuilib.actions.ToolItemAction;
import com.android.ddmuilib.explorer.DeviceExplorer;
import com.android.ddmuilib.handler.BaseFileHandler;
import com.android.ddmuilib.handler.MethodProfilingHandler;
import com.android.ddmuilib.log.event.EventLogPanel;
import com.android.ddmuilib.logcat.LogColors;
import com.android.ddmuilib.logcat.LogFilter;
import com.android.ddmuilib.logcat.LogPanel;
import com.android.ddmuilib.logcat.LogPanel.ILogFilterStorageManager;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.PreferenceStore;
import org.eclipse.swt.SWT;
import org.eclipse.swt.SWTError;
import org.eclipse.swt.SWTException;
import org.eclipse.swt.dnd.Clipboard;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.ControlListener;
import org.eclipse.swt.events.MenuAdapter;
import org.eclipse.swt.events.MenuEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.ShellEvent;
import org.eclipse.swt.events.ShellListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.swt.widgets.Sash;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.TabFolder;
import org.eclipse.swt.widgets.TabItem;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import java.io.File;
import java.util.ArrayList;
/**
* This acts as the UI builder. This cannot be its own thread since this prevent using AWT in an
* SWT application. So this class mainly builds the ui, and manages communication between the panels
* when {@link IDevice} / {@link Client} selection changes.
*/
public class UIThread implements IUiSelectionListener, IClientChangeListener {
/*
* UI tab panel definitions. The constants here must match up with the array
* indices in mPanels. PANEL_CLIENT_LIST is a "virtual" panel representing
* the client list.
*/
public static final int PANEL_CLIENT_LIST = -1;
public static final int PANEL_INFO = 0;
public static final int PANEL_THREAD = 1;
public static final int PANEL_HEAP = 2;
public static final int PANEL_NATIVE_HEAP = 3;
private static final int PANEL_ALLOCATIONS = 4;
private static final int PANEL_SYSINFO = 5;
private static final int PANEL_COUNT = 6;
/** Content is setup in the constructor */
private static TablePanel[] mPanels = new TablePanel[PANEL_COUNT];
private static final String[] mPanelNames = new String[] {
"Info", "Threads", "VM Heap", "Native Heap", "Allocation Tracker", "Sysinfo"
};
private static final String[] mPanelTips = new String[] {
"Client information", "Thread status", "VM heap status",
"Native heap status", "Allocation Tracker", "Sysinfo graphs"
};
private static final String PREFERENCE_LOGSASH =
"logSashLocation"; //$NON-NLS-1$
private static final String PREFERENCE_SASH =
"sashLocation"; //$NON-NLS-1$
private static final String PREFS_COL_TIME =
"logcat.time"; //$NON-NLS-1$
private static final String PREFS_COL_LEVEL =
"logcat.level"; //$NON-NLS-1$
private static final String PREFS_COL_PID =
"logcat.pid"; //$NON-NLS-1$
private static final String PREFS_COL_TAG =
"logcat.tag"; //$NON-NLS-1$
private static final String PREFS_COL_MESSAGE =
"logcat.message"; //$NON-NLS-1$
private static final String PREFS_FILTERS = "logcat.filter"; //$NON-NLS-1$
// singleton instance
private static UIThread mInstance = new UIThread();
// our display
private Display mDisplay;
// the table we show in the left-hand pane
private DevicePanel mDevicePanel;
private IDevice mCurrentDevice = null;
private Client mCurrentClient = null;
// status line at the bottom of the app window
private Label mStatusLine;
// some toolbar items we need to update
private ToolItem mTBShowThreadUpdates;
private ToolItem mTBShowHeapUpdates;
private ToolItem mTBHalt;
private ToolItem mTBCauseGc;
private ToolItem mTBDumpHprof;
private ToolItem mTBProfiling;
private ImageLoader mDdmsImageLoader;
private ImageLoader mDdmuiLibImageLoader;
private final class FilterStorage implements ILogFilterStorageManager {
public LogFilter[] getFilterFromStore() {
String filterPrefs = PrefsDialog.getStore().getString(
PREFS_FILTERS);
// split in a string per filter
String[] filters = filterPrefs.split("\\|"); //$NON-NLS-1$
ArrayList<LogFilter> list =
new ArrayList<LogFilter>(filters.length);
for (String f : filters) {
if (f.length() > 0) {
LogFilter logFilter = new LogFilter();
if (logFilter.loadFromString(f)) {
list.add(logFilter);
}
}
}
return list.toArray(new LogFilter[list.size()]);
}
public void saveFilters(LogFilter[] filters) {
StringBuilder sb = new StringBuilder();
for (LogFilter f : filters) {
String filterString = f.toString();
sb.append(filterString);
sb.append('|');
}
PrefsDialog.getStore().setValue(PREFS_FILTERS, sb.toString());
}
public boolean requiresDefaultFilter() {
return true;
}
}
private LogPanel mLogPanel;
private ToolItemAction mCreateFilterAction;
private ToolItemAction mDeleteFilterAction;
private ToolItemAction mEditFilterAction;
private ToolItemAction mExportAction;
private ToolItemAction mClearAction;
private ToolItemAction[] mLogLevelActions;
private String[] mLogLevelIcons = {
"v.png", //$NON-NLS-1S
"d.png", //$NON-NLS-1S
"i.png", //$NON-NLS-1S
"w.png", //$NON-NLS-1S
"e.png", //$NON-NLS-1S
};
protected Clipboard mClipboard;
private MenuItem mCopyMenuItem;
private MenuItem mSelectAllMenuItem;
private TableFocusListener mTableListener;
private DeviceExplorer mExplorer = null;
private Shell mExplorerShell = null;
private EmulatorControlPanel mEmulatorPanel;
private EventLogPanel mEventLogPanel;
private Image mTracingStartImage;
private Image mTracingStopImage;
private class TableFocusListener implements ITableFocusListener {
private IFocusedTableActivator mCurrentActivator;
public void focusGained(IFocusedTableActivator activator) {
mCurrentActivator = activator;
if (mCopyMenuItem.isDisposed() == false) {
mCopyMenuItem.setEnabled(true);
mSelectAllMenuItem.setEnabled(true);
}
}
public void focusLost(IFocusedTableActivator activator) {
// if we move from one table to another, it's unclear
// if the old table lose its focus before the new
// one gets the focus, so we need to check.
if (activator == mCurrentActivator) {
activator = null;
if (mCopyMenuItem.isDisposed() == false) {
mCopyMenuItem.setEnabled(false);
mSelectAllMenuItem.setEnabled(false);
}
}
}
public void copy(Clipboard clipboard) {
if (mCurrentActivator != null) {
mCurrentActivator.copy(clipboard);
}
}
public void selectAll() {
if (mCurrentActivator != null) {
mCurrentActivator.selectAll();
}
}
}
/**
* Handler for HPROF dumps.
* This will always prompt the user to save the HPROF file.
*/
private class HProfHandler extends BaseFileHandler implements IHprofDumpHandler {
public HProfHandler(Shell parentShell) {
super(parentShell);
}
public void onFailure(final Client client) {
mDisplay.asyncExec(new Runnable() {
public void run() {
try {
displayError("Unable to create HPROF file for application '%1$s'.\n" +
"Check logcat for more information.",
client.getClientData().getClientDescription());
} finally {
// this will make sure the dump hprof button is re-enabled for the
// current selection. as the client is finished dumping an hprof file
enableButtons();
}
}
});
}
public void onSuccess(final String remoteFilePath, final Client client) {
mDisplay.asyncExec(new Runnable() {
public void run() {
final IDevice device = client.getDevice();
try {
// get the sync service to pull the HPROF file
final SyncService sync = client.getDevice().getSyncService();
if (sync != null) {
SyncResult result = promptAndPull(sync,
client.getClientData().getClientDescription() + ".hprof",
remoteFilePath, "Save HPROF file");
if (result != null && result.getCode() != SyncService.RESULT_OK) {
displayError(
"Unable to download HPROF file from device '%1$s'.\n\n%2$s",
device.getSerialNumber(), result.getMessage());
}
} else {
displayError("Unable to download HPROF file from device '%1$s'.",
device.getSerialNumber());
}
} catch (Exception e) {
displayError("Unable to download HPROF file from device '%1$s'.",
device.getSerialNumber());
} finally {
// this will make sure the dump hprof button is re-enabled for the
// current selection. as the client is finished dumping an hprof file
enableButtons();
}
}
});
}
private void displayError(String format, Object... args) {
MessageDialog.openError(mParentShell, "HPROF Error",
String.format(format, args));
}
}
/**
* Generic constructor.
*/
private UIThread() {
mPanels[PANEL_INFO] = new InfoPanel();
mPanels[PANEL_THREAD] = new ThreadPanel();
mPanels[PANEL_HEAP] = new HeapPanel();
if (PrefsDialog.getStore().getBoolean(PrefsDialog.SHOW_NATIVE_HEAP)) {
mPanels[PANEL_NATIVE_HEAP] = new NativeHeapPanel();
} else {
mPanels[PANEL_NATIVE_HEAP] = null;
}
mPanels[PANEL_ALLOCATIONS] = new AllocationPanel();
mPanels[PANEL_SYSINFO] = new SysinfoPanel();
}
/**
* Get singleton instance of the UI thread.
*/
public static UIThread getInstance() {
return mInstance;
}
/**
* Return the Display. Don't try this unless you're in the UI thread.
*/
public Display getDisplay() {
return mDisplay;
}
public void asyncExec(Runnable r) {
if (mDisplay != null && mDisplay.isDisposed() == false) {
mDisplay.asyncExec(r);
}
}
/** returns the IPreferenceStore */
public IPreferenceStore getStore() {
return PrefsDialog.getStore();
}
/**
* Create SWT objects and drive the user interface event loop.
* @param location location of the folder that contains ddms.
*/
public void runUI(String ddmsParentLocation) {
Display.setAppName("ddms");
mDisplay = new Display();
final Shell shell = new Shell(mDisplay);
// create the image loaders for DDMS and DDMUILIB
mDdmsImageLoader = new ImageLoader(this.getClass());
mDdmuiLibImageLoader = new ImageLoader(DevicePanel.class);
shell.setImage(ImageHelper.loadImage(mDdmsImageLoader, mDisplay,
"ddms-icon.png", //$NON-NLS-1$
100, 50, null));
Log.setLogOutput(new ILogOutput() {
public void printAndPromptLog(final LogLevel logLevel, final String tag,
final String message) {
Log.printLog(logLevel, tag, message);
// dialog box only run in UI thread..
mDisplay.asyncExec(new Runnable() {
public void run() {
Shell shell = mDisplay.getActiveShell();
if (logLevel == LogLevel.ERROR) {
MessageDialog.openError(shell, tag, message);
} else {
MessageDialog.openWarning(shell, tag, message);
}
}
});
}
public void printLog(LogLevel logLevel, String tag, String message) {
Log.printLog(logLevel, tag, message);
}
});
// set the handler for hprof dump
ClientData.setHprofDumpHandler(new HProfHandler(shell));
ClientData.setMethodProfilingHandler(new MethodProfilingHandler(shell));
// [try to] ensure ADB is running
String adbLocation;
if (ddmsParentLocation != null && ddmsParentLocation.length() != 0) {
adbLocation = ddmsParentLocation + File.separator + "adb"; //$NON-NLS-1$
} else {
adbLocation = "adb"; //$NON-NLS-1$
}
AndroidDebugBridge.init(true /* debugger support */);
AndroidDebugBridge.createBridge(adbLocation, true /* forceNewBridge */);
// we need to listen to client change to be notified of client status (profiling) change
AndroidDebugBridge.addClientChangeListener(this);
shell.setText("Dalvik Debug Monitor");
setConfirmClose(shell);
createMenus(shell);
createWidgets(shell);
shell.pack();
setSizeAndPosition(shell);
shell.open();
Log.d("ddms", "UI is up");
while (!shell.isDisposed()) {
if (!mDisplay.readAndDispatch())
mDisplay.sleep();
}
mLogPanel.stopLogCat(true);
mDevicePanel.dispose();
for (TablePanel panel : mPanels) {
if (panel != null) {
panel.dispose();
}
}
mDisplay.dispose();
Log.d("ddms", "UI is down");
}
/**
* Set the size and position of the main window from the preference, and
* setup listeners for control events (resize/move of the window)
*/
private void setSizeAndPosition(final Shell shell) {
shell.setMinimumSize(400, 200);
// get the x/y and w/h from the prefs
PreferenceStore prefs = PrefsDialog.getStore();
int x = prefs.getInt(PrefsDialog.SHELL_X);
int y = prefs.getInt(PrefsDialog.SHELL_Y);
int w = prefs.getInt(PrefsDialog.SHELL_WIDTH);
int h = prefs.getInt(PrefsDialog.SHELL_HEIGHT);
// check that we're not out of the display area
Rectangle rect = mDisplay.getClientArea();
// first check the width/height
if (w > rect.width) {
w = rect.width;
prefs.setValue(PrefsDialog.SHELL_WIDTH, rect.width);
}
if (h > rect.height) {
h = rect.height;
prefs.setValue(PrefsDialog.SHELL_HEIGHT, rect.height);
}
// then check x. Make sure the left corner is in the screen
if (x < rect.x) {
x = rect.x;
prefs.setValue(PrefsDialog.SHELL_X, rect.x);
} else if (x >= rect.x + rect.width) {
x = rect.x + rect.width - w;
prefs.setValue(PrefsDialog.SHELL_X, rect.x);
}
// then check y. Make sure the left corner is in the screen
if (y < rect.y) {
y = rect.y;
prefs.setValue(PrefsDialog.SHELL_Y, rect.y);
} else if (y >= rect.y + rect.height) {
y = rect.y + rect.height - h;
prefs.setValue(PrefsDialog.SHELL_Y, rect.y);
}
// now we can set the location/size
shell.setBounds(x, y, w, h);
// add listener for resize/move
shell.addControlListener(new ControlListener() {
public void controlMoved(ControlEvent e) {
// get the new x/y
Rectangle rect = shell.getBounds();
// store in pref file
PreferenceStore prefs = PrefsDialog.getStore();
prefs.setValue(PrefsDialog.SHELL_X, rect.x);
prefs.setValue(PrefsDialog.SHELL_Y, rect.y);
}
public void controlResized(ControlEvent e) {
// get the new w/h
Rectangle rect = shell.getBounds();
// store in pref file
PreferenceStore prefs = PrefsDialog.getStore();
prefs.setValue(PrefsDialog.SHELL_WIDTH, rect.width);
prefs.setValue(PrefsDialog.SHELL_HEIGHT, rect.height);
}
});
}
/**
* Set the size and position of the file explorer window from the
* preference, and setup listeners for control events (resize/move of
* the window)
*/
private void setExplorerSizeAndPosition(final Shell shell) {
shell.setMinimumSize(400, 200);
// get the x/y and w/h from the prefs
PreferenceStore prefs = PrefsDialog.getStore();
int x = prefs.getInt(PrefsDialog.EXPLORER_SHELL_X);
int y = prefs.getInt(PrefsDialog.EXPLORER_SHELL_Y);
int w = prefs.getInt(PrefsDialog.EXPLORER_SHELL_WIDTH);
int h = prefs.getInt(PrefsDialog.EXPLORER_SHELL_HEIGHT);
// check that we're not out of the display area
Rectangle rect = mDisplay.getClientArea();
// first check the width/height
if (w > rect.width) {
w = rect.width;
prefs.setValue(PrefsDialog.EXPLORER_SHELL_WIDTH, rect.width);
}
if (h > rect.height) {
h = rect.height;
prefs.setValue(PrefsDialog.EXPLORER_SHELL_HEIGHT, rect.height);
}
// then check x. Make sure the left corner is in the screen
if (x < rect.x) {
x = rect.x;
prefs.setValue(PrefsDialog.EXPLORER_SHELL_X, rect.x);
} else if (x >= rect.x + rect.width) {
x = rect.x + rect.width - w;
prefs.setValue(PrefsDialog.EXPLORER_SHELL_X, rect.x);
}
// then check y. Make sure the left corner is in the screen
if (y < rect.y) {
y = rect.y;
prefs.setValue(PrefsDialog.EXPLORER_SHELL_Y, rect.y);
} else if (y >= rect.y + rect.height) {
y = rect.y + rect.height - h;
prefs.setValue(PrefsDialog.EXPLORER_SHELL_Y, rect.y);
}
// now we can set the location/size
shell.setBounds(x, y, w, h);
// add listener for resize/move
shell.addControlListener(new ControlListener() {
public void controlMoved(ControlEvent e) {
// get the new x/y
Rectangle rect = shell.getBounds();
// store in pref file
PreferenceStore prefs = PrefsDialog.getStore();
prefs.setValue(PrefsDialog.EXPLORER_SHELL_X, rect.x);
prefs.setValue(PrefsDialog.EXPLORER_SHELL_Y, rect.y);
}
public void controlResized(ControlEvent e) {
// get the new w/h
Rectangle rect = shell.getBounds();
// store in pref file
PreferenceStore prefs = PrefsDialog.getStore();
prefs.setValue(PrefsDialog.EXPLORER_SHELL_WIDTH, rect.width);
prefs.setValue(PrefsDialog.EXPLORER_SHELL_HEIGHT, rect.height);
}
});
}
/*
* Set the confirm-before-close dialog. TODO: enable/disable in prefs. TODO:
* is there any point in having this?
*/
private void setConfirmClose(final Shell shell) {
if (true)
return;
shell.addListener(SWT.Close, new Listener() {
public void handleEvent(Event event) {
int style = SWT.APPLICATION_MODAL | SWT.YES | SWT.NO;
MessageBox msgBox = new MessageBox(shell, style);
msgBox.setText("Confirm...");
msgBox.setMessage("Close DDM?");
event.doit = (msgBox.open() == SWT.YES);
}
});
}
/*
* Create the menu bar and items.
*/
private void createMenus(final Shell shell) {
// create menu bar
Menu menuBar = new Menu(shell, SWT.BAR);
// create top-level items
MenuItem fileItem = new MenuItem(menuBar, SWT.CASCADE);
fileItem.setText("&File");
MenuItem editItem = new MenuItem(menuBar, SWT.CASCADE);
editItem.setText("&Edit");
MenuItem actionItem = new MenuItem(menuBar, SWT.CASCADE);
actionItem.setText("&Actions");
MenuItem deviceItem = new MenuItem(menuBar, SWT.CASCADE);
deviceItem.setText("&Device");
MenuItem helpItem = new MenuItem(menuBar, SWT.CASCADE);
helpItem.setText("&Help");
// create top-level menus
Menu fileMenu = new Menu(menuBar);
fileItem.setMenu(fileMenu);
Menu editMenu = new Menu(menuBar);
editItem.setMenu(editMenu);
Menu actionMenu = new Menu(menuBar);
actionItem.setMenu(actionMenu);
Menu deviceMenu = new Menu(menuBar);
deviceItem.setMenu(deviceMenu);
Menu helpMenu = new Menu(menuBar);
helpItem.setMenu(helpMenu);
MenuItem item;
// create File menu items
item = new MenuItem(fileMenu, SWT.NONE);
item.setText("&Preferences...");
item.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
PrefsDialog.run(shell);
}
});
item = new MenuItem(fileMenu, SWT.NONE);
item.setText("&Static Port Configuration...");
item.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
StaticPortConfigDialog dlg = new StaticPortConfigDialog(shell);
dlg.open();
}
});
new MenuItem(fileMenu, SWT.SEPARATOR);
item = new MenuItem(fileMenu, SWT.NONE);
item.setText("E&xit\tCtrl-Q");
item.setAccelerator('Q' | SWT.CONTROL);
item.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
shell.close();
}
});
// create edit menu items
mCopyMenuItem = new MenuItem(editMenu, SWT.NONE);
mCopyMenuItem.setText("&Copy\tCtrl-C");
mCopyMenuItem.setAccelerator('C' | SWT.COMMAND);
mCopyMenuItem.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
mTableListener.copy(mClipboard);
}
});
new MenuItem(editMenu, SWT.SEPARATOR);
mSelectAllMenuItem = new MenuItem(editMenu, SWT.NONE);
mSelectAllMenuItem.setText("Select &All\tCtrl-A");
mSelectAllMenuItem.setAccelerator('A' | SWT.COMMAND);
mSelectAllMenuItem.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
mTableListener.selectAll();
}
});
// create Action menu items
// TODO: this should come with a confirmation dialog
final MenuItem actionHaltItem = new MenuItem(actionMenu, SWT.NONE);
actionHaltItem.setText("&Halt VM");
actionHaltItem.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
mDevicePanel.killSelectedClient();
}
});
final MenuItem actionCauseGcItem = new MenuItem(actionMenu, SWT.NONE);
actionCauseGcItem.setText("Cause &GC");
actionCauseGcItem.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
mDevicePanel.forceGcOnSelectedClient();
}
});
// configure Action items based on current state
actionMenu.addMenuListener(new MenuAdapter() {
@Override
public void menuShown(MenuEvent e) {
actionHaltItem.setEnabled(mTBHalt.getEnabled() && mCurrentClient != null);
actionCauseGcItem.setEnabled(mTBCauseGc.getEnabled() && mCurrentClient != null);
}
});
// create Device menu items
final MenuItem screenShotItem = new MenuItem(deviceMenu, SWT.NONE);
screenShotItem.setText("&Screen capture...\tCTrl-S");
screenShotItem.setAccelerator('S' | SWT.CONTROL);
screenShotItem.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
if (mCurrentDevice != null) {
ScreenShotDialog dlg = new ScreenShotDialog(shell);
dlg.open(mCurrentDevice);
}
}
});
new MenuItem(deviceMenu, SWT.SEPARATOR);
final MenuItem explorerItem = new MenuItem(deviceMenu, SWT.NONE);
explorerItem.setText("File Explorer...");
explorerItem.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
createFileExplorer();
}
});
new MenuItem(deviceMenu, SWT.SEPARATOR);
final MenuItem processItem = new MenuItem(deviceMenu, SWT.NONE);
processItem.setText("Show &process status...");
processItem.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
DeviceCommandDialog dlg;
dlg = new DeviceCommandDialog("ps -x", "ps-x.txt", shell);
dlg.open(mCurrentDevice);
}
});
final MenuItem deviceStateItem = new MenuItem(deviceMenu, SWT.NONE);
deviceStateItem.setText("Dump &device state...");
deviceStateItem.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
DeviceCommandDialog dlg;
dlg = new DeviceCommandDialog("/system/bin/dumpstate /proc/self/fd/0",
"device-state.txt", shell);
dlg.open(mCurrentDevice);
}
});
final MenuItem appStateItem = new MenuItem(deviceMenu, SWT.NONE);
appStateItem.setText("Dump &app state...");
appStateItem.setEnabled(false);
appStateItem.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
DeviceCommandDialog dlg;
dlg = new DeviceCommandDialog("dumpsys", "app-state.txt", shell);
dlg.open(mCurrentDevice);
}
});
final MenuItem radioStateItem = new MenuItem(deviceMenu, SWT.NONE);
radioStateItem.setText("Dump &radio state...");
radioStateItem.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
DeviceCommandDialog dlg;
dlg = new DeviceCommandDialog(
"cat /data/logs/radio.4 /data/logs/radio.3"
+ " /data/logs/radio.2 /data/logs/radio.1"
+ " /data/logs/radio",
"radio-state.txt", shell);
dlg.open(mCurrentDevice);
}
});
final MenuItem logCatItem = new MenuItem(deviceMenu, SWT.NONE);
logCatItem.setText("Run &logcat...");
logCatItem.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
DeviceCommandDialog dlg;
dlg = new DeviceCommandDialog("logcat '*:d jdwp:w'", "log.txt",
shell);
dlg.open(mCurrentDevice);
}
});
// configure Action items based on current state
deviceMenu.addMenuListener(new MenuAdapter() {
@Override
public void menuShown(MenuEvent e) {
boolean deviceEnabled = mCurrentDevice != null;
screenShotItem.setEnabled(deviceEnabled);
explorerItem.setEnabled(deviceEnabled);
processItem.setEnabled(deviceEnabled);
deviceStateItem.setEnabled(deviceEnabled);
appStateItem.setEnabled(deviceEnabled);
radioStateItem.setEnabled(deviceEnabled);
logCatItem.setEnabled(deviceEnabled);
}
});
// create Help menu items
item = new MenuItem(helpMenu, SWT.NONE);
item.setText("&Contents...");
item.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
int style = SWT.APPLICATION_MODAL | SWT.OK;
MessageBox msgBox = new MessageBox(shell, style);
msgBox.setText("Help!");
msgBox.setMessage("Help wanted.");
msgBox.open();
}
});
new MenuItem(helpMenu, SWT.SEPARATOR);
item = new MenuItem(helpMenu, SWT.NONE);
item.setText("&About...");
item.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
AboutDialog dlg = new AboutDialog(shell);
dlg.open();
}
});
// tell the shell to use this menu
shell.setMenuBar(menuBar);
}
/*
* Create the widgets in the main application window. The basic layout is a
* two-panel sash, with a scrolling list of VMs on the left and detailed
* output for a single VM on the right.
*/
private void createWidgets(final Shell shell) {
Color darkGray = shell.getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY);
/*
* Create three areas: tool bar, split panels, status line
*/
shell.setLayout(new GridLayout(1, false));
// 1. panel area
final Composite panelArea = new Composite(shell, SWT.BORDER);
// make the panel area absorb all space
panelArea.setLayoutData(new GridData(GridData.FILL_BOTH));
// 2. status line.
mStatusLine = new Label(shell, SWT.NONE);
// make status line extend all the way across
mStatusLine.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
mStatusLine.setText("Initializing...");
/*
* Configure the split-panel area.
*/
final PreferenceStore prefs = PrefsDialog.getStore();
Composite topPanel = new Composite(panelArea, SWT.NONE);
final Sash sash = new Sash(panelArea, SWT.HORIZONTAL);
sash.setBackground(darkGray);
Composite bottomPanel = new Composite(panelArea, SWT.NONE);
panelArea.setLayout(new FormLayout());
createTopPanel(topPanel, darkGray);
createBottomPanel(bottomPanel);
// form layout data
FormData data = new FormData();
data.top = new FormAttachment(0, 0);
data.bottom = new FormAttachment(sash, 0);
data.left = new FormAttachment(0, 0);
data.right = new FormAttachment(100, 0);
topPanel.setLayoutData(data);
final FormData sashData = new FormData();
if (prefs != null && prefs.contains(PREFERENCE_LOGSASH)) {
sashData.top = new FormAttachment(0, prefs.getInt(
PREFERENCE_LOGSASH));
} else {
sashData.top = new FormAttachment(50,0); // 50% across
}
sashData.left = new FormAttachment(0, 0);
sashData.right = new FormAttachment(100, 0);
sash.setLayoutData(sashData);
data = new FormData();
data.top = new FormAttachment(sash, 0);
data.bottom = new FormAttachment(100, 0);
data.left = new FormAttachment(0, 0);
data.right = new FormAttachment(100, 0);
bottomPanel.setLayoutData(data);
// allow resizes, but cap at minPanelWidth
sash.addListener(SWT.Selection, new Listener() {
public void handleEvent(Event e) {
Rectangle sashRect = sash.getBounds();
Rectangle panelRect = panelArea.getClientArea();
int bottom = panelRect.height - sashRect.height - 100;
e.y = Math.max(Math.min(e.y, bottom), 100);
if (e.y != sashRect.y) {
sashData.top = new FormAttachment(0, e.y);
prefs.setValue(PREFERENCE_LOGSASH, e.y);
panelArea.layout();
}
}
});
// add a global focus listener for all the tables
mTableListener = new TableFocusListener();
// now set up the listener in the various panels
mLogPanel.setTableFocusListener(mTableListener);
mEventLogPanel.setTableFocusListener(mTableListener);
for (TablePanel p : mPanels) {
if (p != null) {
p.setTableFocusListener(mTableListener);
}
}
mStatusLine.setText("");
}
/*
* Populate the tool bar.
*/
private void createDevicePanelToolBar(ToolBar toolBar) {
Display display = toolBar.getDisplay();
// add "show heap updates" button
mTBShowHeapUpdates = new ToolItem(toolBar, SWT.CHECK);
mTBShowHeapUpdates.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, display,
DevicePanel.ICON_HEAP, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
mTBShowHeapUpdates.setToolTipText("Show heap updates");
mTBShowHeapUpdates.setEnabled(false);
mTBShowHeapUpdates.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
if (mCurrentClient != null) {
// boolean status = ((ToolItem)e.item).getSelection();
// invert previous state
boolean enable = !mCurrentClient.isHeapUpdateEnabled();
mCurrentClient.setHeapUpdateEnabled(enable);
} else {
e.doit = false; // this has no effect?
}
}
});
// add "dump HPROF" button
mTBDumpHprof = new ToolItem(toolBar, SWT.PUSH);
mTBDumpHprof.setToolTipText("Dump HPROF file");
mTBDumpHprof.setEnabled(false);
mTBDumpHprof.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, display,
DevicePanel.ICON_HPROF, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
mTBDumpHprof.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
mDevicePanel.dumpHprof();
// this will make sure the dump hprof button is disabled for the current selection
// as the client is already dumping an hprof file
enableButtons();
}
});
// add "cause GC" button
mTBCauseGc = new ToolItem(toolBar, SWT.PUSH);
mTBCauseGc.setToolTipText("Cause an immediate GC");
mTBCauseGc.setEnabled(false);
mTBCauseGc.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, display,
DevicePanel.ICON_GC, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
mTBCauseGc.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
mDevicePanel.forceGcOnSelectedClient();
}
});
new ToolItem(toolBar, SWT.SEPARATOR);
// add "show thread updates" button
mTBShowThreadUpdates = new ToolItem(toolBar, SWT.CHECK);
mTBShowThreadUpdates.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, display,
DevicePanel.ICON_THREAD, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
mTBShowThreadUpdates.setToolTipText("Show thread updates");
mTBShowThreadUpdates.setEnabled(false);
mTBShowThreadUpdates.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
if (mCurrentClient != null) {
// boolean status = ((ToolItem)e.item).getSelection();
// invert previous state
boolean enable = !mCurrentClient.isThreadUpdateEnabled();
mCurrentClient.setThreadUpdateEnabled(enable);
} else {
e.doit = false; // this has no effect?
}
}
});
// add a start/stop method tracing
mTracingStartImage = ImageHelper.loadImage(mDdmuiLibImageLoader, display,
DevicePanel.ICON_TRACING_START,
DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null);
mTracingStopImage = ImageHelper.loadImage(mDdmuiLibImageLoader, display,
DevicePanel.ICON_TRACING_STOP,
DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null);
mTBProfiling = new ToolItem(toolBar, SWT.PUSH);
mTBProfiling.setToolTipText("Start Method Profiling");
mTBProfiling.setEnabled(false);
mTBProfiling.setImage(mTracingStartImage);
mTBProfiling.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
mDevicePanel.toggleMethodProfiling();
}
});
new ToolItem(toolBar, SWT.SEPARATOR);
// add "kill VM" button; need to make this visually distinct from
// the status update buttons
mTBHalt = new ToolItem(toolBar, SWT.PUSH);
mTBHalt.setToolTipText("Halt the target VM");
mTBHalt.setEnabled(false);
mTBHalt.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, display,
DevicePanel.ICON_HALT, DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
mTBHalt.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
mDevicePanel.killSelectedClient();
}
});
toolBar.pack();
}
private void createTopPanel(final Composite comp, Color darkGray) {
final PreferenceStore prefs = PrefsDialog.getStore();
comp.setLayout(new FormLayout());
Composite leftPanel = new Composite(comp, SWT.NONE);
final Sash sash = new Sash(comp, SWT.VERTICAL);
sash.setBackground(darkGray);
Composite rightPanel = new Composite(comp, SWT.NONE);
createLeftPanel(leftPanel);
createRightPanel(rightPanel);
FormData data = new FormData();
data.top = new FormAttachment(0, 0);
data.bottom = new FormAttachment(100, 0);
data.left = new FormAttachment(0, 0);
data.right = new FormAttachment(sash, 0);
leftPanel.setLayoutData(data);
final FormData sashData = new FormData();
sashData.top = new FormAttachment(0, 0);
sashData.bottom = new FormAttachment(100, 0);
if (prefs != null && prefs.contains(PREFERENCE_SASH)) {
sashData.left = new FormAttachment(0, prefs.getInt(
PREFERENCE_SASH));
} else {
// position the sash 380 from the right instead of x% (done by using
// FormAttachment(x, 0)) in order to keep the sash at the same
// position
// from the left when the window is resized.
// 380px is just enough to display the left table with no horizontal
// scrollbar with the default font.
sashData.left = new FormAttachment(0, 380);
}
sash.setLayoutData(sashData);
data = new FormData();
data.top = new FormAttachment(0, 0);
data.bottom = new FormAttachment(100, 0);
data.left = new FormAttachment(sash, 0);
data.right = new FormAttachment(100, 0);
rightPanel.setLayoutData(data);
final int minPanelWidth = 60;
// allow resizes, but cap at minPanelWidth
sash.addListener(SWT.Selection, new Listener() {
public void handleEvent(Event e) {
Rectangle sashRect = sash.getBounds();
Rectangle panelRect = comp.getClientArea();
int right = panelRect.width - sashRect.width - minPanelWidth;
e.x = Math.max(Math.min(e.x, right), minPanelWidth);
if (e.x != sashRect.x) {
sashData.left = new FormAttachment(0, e.x);
prefs.setValue(PREFERENCE_SASH, e.x);
comp.layout();
}
}
});
}
private void createBottomPanel(final Composite comp) {
final PreferenceStore prefs = PrefsDialog.getStore();
// create clipboard
Display display = comp.getDisplay();
mClipboard = new Clipboard(display);
LogColors colors = new LogColors();
colors.infoColor = new Color(display, 0, 127, 0);
colors.debugColor = new Color(display, 0, 0, 127);
colors.errorColor = new Color(display, 255, 0, 0);
colors.warningColor = new Color(display, 255, 127, 0);
colors.verboseColor = new Color(display, 0, 0, 0);
// set the preferences names
LogPanel.PREFS_TIME = PREFS_COL_TIME;
LogPanel.PREFS_LEVEL = PREFS_COL_LEVEL;
LogPanel.PREFS_PID = PREFS_COL_PID;
LogPanel.PREFS_TAG = PREFS_COL_TAG;
LogPanel.PREFS_MESSAGE = PREFS_COL_MESSAGE;
comp.setLayout(new GridLayout(1, false));
ToolBar toolBar = new ToolBar(comp, SWT.HORIZONTAL);
mCreateFilterAction = new ToolItemAction(toolBar, SWT.PUSH);
mCreateFilterAction.item.setToolTipText("Create Filter");
mCreateFilterAction.item.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, mDisplay,
"add.png", //$NON-NLS-1$
DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
mCreateFilterAction.item.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
mLogPanel.addFilter();
}
});
mEditFilterAction = new ToolItemAction(toolBar, SWT.PUSH);
mEditFilterAction.item.setToolTipText("Edit Filter");
mEditFilterAction.item.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, mDisplay,
"edit.png", //$NON-NLS-1$
DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
mEditFilterAction.item.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
mLogPanel.editFilter();
}
});
mDeleteFilterAction = new ToolItemAction(toolBar, SWT.PUSH);
mDeleteFilterAction.item.setToolTipText("Delete Filter");
mDeleteFilterAction.item.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, mDisplay,
"delete.png", //$NON-NLS-1$
DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
mDeleteFilterAction.item.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
mLogPanel.deleteFilter();
}
});
new ToolItem(toolBar, SWT.SEPARATOR);
LogLevel[] levels = LogLevel.values();
mLogLevelActions = new ToolItemAction[mLogLevelIcons.length];
for (int i = 0 ; i < mLogLevelActions.length; i++) {
String name = levels[i].getStringValue();
final ToolItemAction newAction = new ToolItemAction(toolBar, SWT.CHECK);
mLogLevelActions[i] = newAction;
//newAction.item.setText(name);
newAction.item.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
// disable the other actions and record current index
for (int i = 0 ; i < mLogLevelActions.length; i++) {
ToolItemAction a = mLogLevelActions[i];
if (a == newAction) {
a.setChecked(true);
// set the log level
mLogPanel.setCurrentFilterLogLevel(i+2);
} else {
a.setChecked(false);
}
}
}
});
newAction.item.setToolTipText(name);
newAction.item.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, mDisplay,
mLogLevelIcons[i],
DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
}
new ToolItem(toolBar, SWT.SEPARATOR);
mClearAction = new ToolItemAction(toolBar, SWT.PUSH);
mClearAction.item.setToolTipText("Clear Log");
mClearAction.item.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, mDisplay,
"clear.png", //$NON-NLS-1$
DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
mClearAction.item.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
mLogPanel.clear();
}
});
new ToolItem(toolBar, SWT.SEPARATOR);
mExportAction = new ToolItemAction(toolBar, SWT.PUSH);
mExportAction.item.setToolTipText("Export Selection As Text...");
mExportAction.item.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, mDisplay,
"save.png", //$NON-NLS-1$
DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
mExportAction.item.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
mLogPanel.save();
}
});
toolBar.pack();
// now create the log view
mLogPanel = new LogPanel(new ImageLoader(LogPanel.class), colors, new FilterStorage(),
LogPanel.FILTER_MANUAL);
mLogPanel.setActions(mDeleteFilterAction, mEditFilterAction, mLogLevelActions);
String colMode = prefs.getString(PrefsDialog.LOGCAT_COLUMN_MODE);
if (PrefsDialog.LOGCAT_COLUMN_MODE_AUTO.equals(colMode)) {
mLogPanel.setColumnMode(LogPanel.COLUMN_MODE_AUTO);
}
String fontStr = PrefsDialog.getStore().getString(PrefsDialog.LOGCAT_FONT);
if (fontStr != null) {
try {
FontData fdat = new FontData(fontStr);
mLogPanel.setFont(new Font(display, fdat));
} catch (IllegalArgumentException e) {
// Looks like fontStr isn't a valid font representation.
// We do nothing in this case, the logcat view will use the default font.
} catch (SWTError e2) {
// Looks like the Font() constructor failed.
// We do nothing in this case, the logcat view will use the default font.
}
}
mLogPanel.createPanel(comp);
// and start the logcat
mLogPanel.startLogCat(mCurrentDevice);
}
/*
* Create the contents of the left panel: a table of VMs.
*/
private void createLeftPanel(final Composite comp) {
comp.setLayout(new GridLayout(1, false));
ToolBar toolBar = new ToolBar(comp, SWT.HORIZONTAL | SWT.RIGHT | SWT.WRAP);
toolBar.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
createDevicePanelToolBar(toolBar);
Composite c = new Composite(comp, SWT.NONE);
c.setLayoutData(new GridData(GridData.FILL_BOTH));
mDevicePanel = new DevicePanel(new ImageLoader(DevicePanel.class), true /* showPorts */);
mDevicePanel.createPanel(c);
// add ourselves to the device panel selection listener
mDevicePanel.addSelectionListener(this);
}
/*
* Create the contents of the right panel: tabs with VM information.
*/
private void createRightPanel(final Composite comp) {
TabItem item;
TabFolder tabFolder;
comp.setLayout(new FillLayout());
tabFolder = new TabFolder(comp, SWT.NONE);
for (int i = 0; i < mPanels.length; i++) {
if (mPanels[i] != null) {
item = new TabItem(tabFolder, SWT.NONE);
item.setText(mPanelNames[i]);
item.setToolTipText(mPanelTips[i]);
item.setControl(mPanels[i].createPanel(tabFolder));
}
}
// add the emulator control panel to the folders.
item = new TabItem(tabFolder, SWT.NONE);
item.setText("Emulator Control");
item.setToolTipText("Emulator Control Panel");
mEmulatorPanel = new EmulatorControlPanel(mDdmuiLibImageLoader);
item.setControl(mEmulatorPanel.createPanel(tabFolder));
// add the event log panel to the folders.
item = new TabItem(tabFolder, SWT.NONE);
item.setText("Event Log");
item.setToolTipText("Event Log");
// create the composite that will hold the toolbar and the event log panel.
Composite eventLogTopComposite = new Composite(tabFolder, SWT.NONE);
item.setControl(eventLogTopComposite);
eventLogTopComposite.setLayout(new GridLayout(1, false));
// create the toolbar and the actions
ToolBar toolbar = new ToolBar(eventLogTopComposite, SWT.HORIZONTAL);
toolbar.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
ToolItemAction optionsAction = new ToolItemAction(toolbar, SWT.PUSH);
optionsAction.item.setToolTipText("Opens the options panel");
optionsAction.item.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, comp.getDisplay(),
"edit.png", //$NON-NLS-1$
DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
ToolItemAction clearAction = new ToolItemAction(toolbar, SWT.PUSH);
clearAction.item.setToolTipText("Clears the event log");
clearAction.item.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, comp.getDisplay(),
"clear.png", //$NON-NLS-1$
DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
new ToolItem(toolbar, SWT.SEPARATOR);
ToolItemAction saveAction = new ToolItemAction(toolbar, SWT.PUSH);
saveAction.item.setToolTipText("Saves the event log");
saveAction.item.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, comp.getDisplay(),
"save.png", //$NON-NLS-1$
DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
ToolItemAction loadAction = new ToolItemAction(toolbar, SWT.PUSH);
loadAction.item.setToolTipText("Loads an event log");
loadAction.item.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, comp.getDisplay(),
"load.png", //$NON-NLS-1$
DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
ToolItemAction importBugAction = new ToolItemAction(toolbar, SWT.PUSH);
importBugAction.item.setToolTipText("Imports a bug report");
importBugAction.item.setImage(ImageHelper.loadImage(mDdmuiLibImageLoader, comp.getDisplay(),
"importBug.png", //$NON-NLS-1$
DevicePanel.ICON_WIDTH, DevicePanel.ICON_WIDTH, null));
// create the event log panel
mEventLogPanel = new EventLogPanel(mDdmuiLibImageLoader);
// set the external actions
mEventLogPanel.setActions(optionsAction, clearAction, saveAction, loadAction,
importBugAction);
// create the panel
mEventLogPanel.createPanel(eventLogTopComposite);
}
private void createFileExplorer() {
if (mExplorer == null) {
mExplorerShell = new Shell(mDisplay);
// create the ui
mExplorerShell.setLayout(new GridLayout(1, false));
// toolbar + action
ToolBar toolBar = new ToolBar(mExplorerShell, SWT.HORIZONTAL);
toolBar.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
ToolItemAction pullAction = new ToolItemAction(toolBar, SWT.PUSH);
pullAction.item.setToolTipText("Pull File from Device");
Image image = mDdmuiLibImageLoader.loadImage("pull.png", mDisplay); //$NON-NLS-1$
if (image != null) {
pullAction.item.setImage(image);
} else {
// this is for debugging purpose when the icon is missing
pullAction.item.setText("Pull"); //$NON-NLS-1$
}
ToolItemAction pushAction = new ToolItemAction(toolBar, SWT.PUSH);
pushAction.item.setToolTipText("Push file onto Device");
image = mDdmuiLibImageLoader.loadImage("push.png", mDisplay); //$NON-NLS-1$
if (image != null) {
pushAction.item.setImage(image);
} else {
// this is for debugging purpose when the icon is missing
pushAction.item.setText("Push"); //$NON-NLS-1$
}
ToolItemAction deleteAction = new ToolItemAction(toolBar, SWT.PUSH);
deleteAction.item.setToolTipText("Delete");
image = mDdmuiLibImageLoader.loadImage("delete.png", mDisplay); //$NON-NLS-1$
if (image != null) {
deleteAction.item.setImage(image);
} else {
// this is for debugging purpose when the icon is missing
deleteAction.item.setText("Delete"); //$NON-NLS-1$
}
// device explorer
mExplorer = new DeviceExplorer();
mExplorer.setImages(mDdmuiLibImageLoader.loadImage("file.png", mDisplay), //$NON-NLS-1$
mDdmuiLibImageLoader.loadImage("folder.png", mDisplay), //$NON-NLS-1$
mDdmuiLibImageLoader.loadImage("android.png", mDisplay), //$NON-NLS-1$
null);
mExplorer.setActions(pushAction, pullAction, deleteAction);
pullAction.item.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
mExplorer.pullSelection();
}
});
pullAction.setEnabled(false);
pushAction.item.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
mExplorer.pushIntoSelection();
}
});
pushAction.setEnabled(false);
deleteAction.item.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
mExplorer.deleteSelection();
}
});
deleteAction.setEnabled(false);
Composite parent = new Composite(mExplorerShell, SWT.NONE);
parent.setLayoutData(new GridData(GridData.FILL_BOTH));
mExplorer.createPanel(parent);
mExplorer.switchDevice(mCurrentDevice);
mExplorerShell.addShellListener(new ShellListener() {
public void shellActivated(ShellEvent e) {
// pass
}
public void shellClosed(ShellEvent e) {
mExplorer = null;
mExplorerShell = null;
}
public void shellDeactivated(ShellEvent e) {
// pass
}
public void shellDeiconified(ShellEvent e) {
// pass
}
public void shellIconified(ShellEvent e) {
// pass
}
});
mExplorerShell.pack();
setExplorerSizeAndPosition(mExplorerShell);
mExplorerShell.open();
} else {
if (mExplorerShell != null) {
mExplorerShell.forceActive();
}
}
}
/**
* Set the status line. TODO: make this a stack, so we can safely have
* multiple things trying to set it all at once. Also specify an expiration?
*/
public void setStatusLine(final String str) {
try {
mDisplay.asyncExec(new Runnable() {
public void run() {
doSetStatusLine(str);
}
});
} catch (SWTException swte) {
if (!mDisplay.isDisposed())
throw swte;
}
}
private void doSetStatusLine(String str) {
if (mStatusLine.isDisposed())
return;
if (!mStatusLine.getText().equals(str)) {
mStatusLine.setText(str);
// try { Thread.sleep(100); }
// catch (InterruptedException ie) {}
}
}
public void displayError(final String msg) {
try {
mDisplay.syncExec(new Runnable() {
public void run() {
MessageDialog.openError(mDisplay.getActiveShell(), "Error",
msg);
}
});
} catch (SWTException swte) {
if (!mDisplay.isDisposed())
throw swte;
}
}
private void enableButtons() {
if (mCurrentClient != null) {
mTBShowThreadUpdates.setSelection(mCurrentClient.isThreadUpdateEnabled());
mTBShowThreadUpdates.setEnabled(true);
mTBShowHeapUpdates.setSelection(mCurrentClient.isHeapUpdateEnabled());
mTBShowHeapUpdates.setEnabled(true);
mTBHalt.setEnabled(true);
mTBCauseGc.setEnabled(true);
ClientData data = mCurrentClient.getClientData();
if (data.hasFeature(ClientData.FEATURE_HPROF)) {
mTBDumpHprof.setEnabled(data.hasPendingHprofDump() == false);
mTBDumpHprof.setToolTipText("Dump HPROF file");
} else {
mTBDumpHprof.setEnabled(false);
mTBDumpHprof.setToolTipText("Dump HPROF file (not supported by this VM)");
}
if (data.hasFeature(ClientData.FEATURE_PROFILING)) {
mTBProfiling.setEnabled(true);
if (data.getMethodProfilingStatus() == MethodProfilingStatus.ON) {
mTBProfiling.setToolTipText("Stop Method Profiling");
mTBProfiling.setImage(mTracingStopImage);
} else {
mTBProfiling.setToolTipText("Start Method Profiling");
mTBProfiling.setImage(mTracingStartImage);
}
} else {
mTBProfiling.setEnabled(false);
mTBProfiling.setImage(mTracingStartImage);
mTBProfiling.setToolTipText("Start Method Profiling (not supported by this VM)");
}
} else {
// list is empty, disable these
mTBShowThreadUpdates.setSelection(false);
mTBShowThreadUpdates.setEnabled(false);
mTBShowHeapUpdates.setSelection(false);
mTBShowHeapUpdates.setEnabled(false);
mTBHalt.setEnabled(false);
mTBCauseGc.setEnabled(false);
mTBDumpHprof.setEnabled(false);
mTBDumpHprof.setToolTipText("Dump HPROF file");
mTBProfiling.setEnabled(false);
mTBProfiling.setImage(mTracingStartImage);
mTBProfiling.setToolTipText("Start Method Profiling");
}
}
/**
* Sent when a new {@link IDevice} and {@link Client} are selected.
* @param selectedDevice the selected device. If null, no devices are selected.
* @param selectedClient The selected client. If null, no clients are selected.
*
* @see IUiSelectionListener
*/
public void selectionChanged(IDevice selectedDevice, Client selectedClient) {
if (mCurrentDevice != selectedDevice) {
mCurrentDevice = selectedDevice;
for (TablePanel panel : mPanels) {
if (panel != null) {
panel.deviceSelected(mCurrentDevice);
}
}
mEmulatorPanel.deviceSelected(mCurrentDevice);
mLogPanel.deviceSelected(mCurrentDevice);
if (mEventLogPanel != null) {
mEventLogPanel.deviceSelected(mCurrentDevice);
}
if (mExplorer != null) {
mExplorer.switchDevice(mCurrentDevice);
}
}
if (mCurrentClient != selectedClient) {
AndroidDebugBridge.getBridge().setSelectedClient(selectedClient);
mCurrentClient = selectedClient;
for (TablePanel panel : mPanels) {
if (panel != null) {
panel.clientSelected(mCurrentClient);
}
}
enableButtons();
}
}
public void clientChanged(Client client, int changeMask) {
if ((changeMask & Client.CHANGE_METHOD_PROFILING_STATUS) ==
Client.CHANGE_METHOD_PROFILING_STATUS) {
if (mCurrentClient == client) {
mDisplay.asyncExec(new Runnable() {
public void run() {
// force refresh of the button enabled state.
enableButtons();
}
});
}
}
}
}