FileDocCategorySizeDatePackage
UIThread.javaAPI DocAndroid 1.5 API56289Wed May 06 22:41:08 BST 2009com.android.ddms

UIThread

public class UIThread extends Object implements com.android.ddmuilib.DevicePanel.IUiSelectionListener
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 Device} / {@link Client} selection changes.

Fields Summary
public static final int
PANEL_CLIENT_LIST
public static final int
PANEL_INFO
public static final int
PANEL_THREAD
public static final int
PANEL_HEAP
public static final int
PANEL_NATIVE_HEAP
private static final int
PANEL_ALLOCATIONS
private static final int
PANEL_SYSINFO
private static final int
PANEL_COUNT
private static com.android.ddmuilib.TablePanel[]
mPanels
Content is setup in the constructor
private static final String[]
mPanelNames
private static final String[]
mPanelTips
private static final String
PREFERENCE_LOGSASH
private static final String
PREFERENCE_SASH
private static final String
PREFS_COL_TIME
private static final String
PREFS_COL_LEVEL
private static final String
PREFS_COL_PID
private static final String
PREFS_COL_TAG
private static final String
PREFS_COL_MESSAGE
private static final String
PREFS_FILTERS
private static UIThread
mInstance
private org.eclipse.swt.widgets.Display
mDisplay
private com.android.ddmuilib.DevicePanel
mDevicePanel
private com.android.ddmlib.Device
mCurrentDevice
private com.android.ddmlib.Client
mCurrentClient
private org.eclipse.swt.widgets.Label
mStatusLine
private org.eclipse.swt.widgets.ToolItem
mTBShowThreadUpdates
private org.eclipse.swt.widgets.ToolItem
mTBShowHeapUpdates
private org.eclipse.swt.widgets.ToolItem
mTBHalt
private org.eclipse.swt.widgets.ToolItem
mTBCauseGc
private com.android.ddmuilib.ImageLoader
mDdmsImageLoader
private com.android.ddmuilib.ImageLoader
mDdmuiLibImageLoader
private com.android.ddmuilib.logcat.LogPanel
mLogPanel
private com.android.ddmuilib.actions.ToolItemAction
mCreateFilterAction
private com.android.ddmuilib.actions.ToolItemAction
mDeleteFilterAction
private com.android.ddmuilib.actions.ToolItemAction
mEditFilterAction
private com.android.ddmuilib.actions.ToolItemAction
mExportAction
private com.android.ddmuilib.actions.ToolItemAction
mClearAction
private com.android.ddmuilib.actions.ToolItemAction[]
mLogLevelActions
private String[]
mLogLevelIcons
protected org.eclipse.swt.dnd.Clipboard
mClipboard
private org.eclipse.swt.widgets.MenuItem
mCopyMenuItem
private org.eclipse.swt.widgets.MenuItem
mSelectAllMenuItem
private TableFocusListener
mTableListener
private com.android.ddmuilib.explorer.DeviceExplorer
mExplorer
private org.eclipse.swt.widgets.Shell
mExplorerShell
private com.android.ddmuilib.EmulatorControlPanel
mEmulatorPanel
private com.android.ddmuilib.log.event.EventLogPanel
mEventLogPanel
Constructors Summary
private UIThread()
Generic constructor.

        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();
    
Methods Summary
public voidasyncExec(java.lang.Runnable r)

        if (mDisplay != null && mDisplay.isDisposed() == false) {
            mDisplay.asyncExec(r);
        }
    
private voidcreateBottomPanel(org.eclipse.swt.widgets.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);
    
private voidcreateDevicePanelToolBar(org.eclipse.swt.widgets.ToolBar toolBar)

        Display display = toolBar.getDisplay();
        
        // 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 "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?
                }
            }
        });

        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();
            }
        });
        
        new ToolItem(toolBar, SWT.SEPARATOR);

        // add "cause GC" button
        mTBCauseGc = new ToolItem(toolBar, SWT.PUSH);
        mTBCauseGc.setToolTipText("Cause an immediate GC in the target VM");
        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();
            }
        });

       toolBar.pack();
    
private voidcreateFileExplorer()

        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");
            pullAction.item.setImage(mDdmuiLibImageLoader.loadImage("pull.png", mDisplay)); //$NON-NLS-1$

            ToolItemAction pushAction = new ToolItemAction(toolBar, SWT.PUSH);
            pushAction.item.setToolTipText("Push file onto Device");
            pushAction.item.setImage(mDdmuiLibImageLoader.loadImage("push.png", mDisplay)); //$NON-NLS-1$

            ToolItemAction deleteAction = new ToolItemAction(toolBar, SWT.PUSH);
            deleteAction.item.setToolTipText("Delete");
            deleteAction.item.setImage(mDdmuiLibImageLoader.loadImage("delete.png", mDisplay)); //$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();
            }
        }
    
private voidcreateLeftPanel(org.eclipse.swt.widgets.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);
    
private voidcreateMenus(org.eclipse.swt.widgets.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());
                actionCauseGcItem.setEnabled(mTBCauseGc.getEnabled());
            }
        });

        // create Device menu items
        item = new MenuItem(deviceMenu, SWT.NONE);
        item.setText("&Screen capture...\tCTrl-S");
        item.setAccelerator('S" | SWT.CONTROL);
        item.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);

        item = new MenuItem(deviceMenu, SWT.NONE);
        item.setText("File Explorer...");
        item.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                createFileExplorer();
            }
        });

        new MenuItem(deviceMenu, SWT.SEPARATOR);

        item = new MenuItem(deviceMenu, SWT.NONE);
        item.setText("Show &process status...");
        item.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                DeviceCommandDialog dlg;
                dlg = new DeviceCommandDialog("ps -x", "ps-x.txt", shell);
                dlg.open(mCurrentDevice);
            }
        });

        item = new MenuItem(deviceMenu, SWT.NONE);
        item.setText("Dump &device state...");
        item.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);
            }
        });

        item = new MenuItem(deviceMenu, SWT.NONE);
        item.setText("Dump &app state...");
        item.setEnabled(false);
        item.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                DeviceCommandDialog dlg;
                dlg = new DeviceCommandDialog("dumpsys", "app-state.txt", shell);
                dlg.open(mCurrentDevice);
            }
        });

        item = new MenuItem(deviceMenu, SWT.NONE);
        item.setText("Dump &radio state...");
        item.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);
            }
        });

        item = new MenuItem(deviceMenu, SWT.NONE);
        item.setText("Run &logcat...");
        item.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                DeviceCommandDialog dlg;
                dlg = new DeviceCommandDialog("logcat '*:d jdwp:w'", "log.txt",
                        shell);
                dlg.open(mCurrentDevice);
            }
        });

        // 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);
    
private voidcreateRightPanel(org.eclipse.swt.widgets.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 voidcreateTopPanel(org.eclipse.swt.widgets.Composite comp, org.eclipse.swt.graphics.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 voidcreateWidgets(org.eclipse.swt.widgets.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("");
    
public voiddisplayError(java.lang.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 voiddoSetStatusLine(java.lang.String str)

        if (mStatusLine.isDisposed())
            return;

        if (!mStatusLine.getText().equals(str)) {
            mStatusLine.setText(str);

            // try { Thread.sleep(100); }
            // catch (InterruptedException ie) {}
        }
    
private voidenableButtons()

        if (mCurrentClient != null) {
            mTBShowThreadUpdates.setSelection(mCurrentClient.isThreadUpdateEnabled());
            mTBShowThreadUpdates.setEnabled(true);
            mTBShowHeapUpdates.setSelection(mCurrentClient.isHeapUpdateEnabled());
            mTBShowHeapUpdates.setEnabled(true);
            mTBHalt.setEnabled(true);
            mTBCauseGc.setEnabled(true);
        } else {
            // list is empty, disable these
            mTBShowThreadUpdates.setSelection(false);
            mTBShowThreadUpdates.setEnabled(false);
            mTBShowHeapUpdates.setSelection(false);
            mTBShowHeapUpdates.setEnabled(false);
            mTBHalt.setEnabled(false);
            mTBCauseGc.setEnabled(false);
        }
    
public org.eclipse.swt.widgets.DisplaygetDisplay()
Return the Display. Don't try this unless you're in the UI thread.

        return mDisplay;
    
public static com.android.ddms.UIThreadgetInstance()
Get singleton instance of the UI thread.

        return mInstance;
    
public org.eclipse.jface.preference.IPreferenceStoregetStore()
returns the IPreferenceStore

        return PrefsDialog.getStore();
    
public voidrunUI()
Create SWT objects and drive the user interface event loop.

        Display.setAppName("ddms");
        mDisplay = new Display();
        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);
            }
        });

        // [try to] ensure ADB is running
        String adbLocation = System.getProperty("com.android.ddms.bindir"); //$NON-NLS-1$
        if (adbLocation != null && adbLocation.length() != 0) {
            adbLocation += File.separator + "adb"; //$NON-NLS-1$
        } else {
            adbLocation = "adb"; //$NON-NLS-1$
        }

        AndroidDebugBridge.init(true /* debugger support */);
        AndroidDebugBridge.createBridge(adbLocation, true /* forceNewBridge */);

        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");
    
public voidselectionChanged(com.android.ddmlib.Device selectedDevice, com.android.ddmlib.Client selectedClient)
Sent when a new {@link Device} 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

        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();
        }
    
private voidsetConfirmClose(org.eclipse.swt.widgets.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);
            }
        });
    
private voidsetExplorerSizeAndPosition(org.eclipse.swt.widgets.Shell shell)
Set the size and position of the file explorer window from the preference, and setup listeners for control events (resize/move of the window)

        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);
            }
        });
    
private voidsetSizeAndPosition(org.eclipse.swt.widgets.Shell shell)
Set the size and position of the main window from the preference, and setup listeners for control events (resize/move of the window)

        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);
            }
        });
    
public voidsetStatusLine(java.lang.String str)
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?

        try {
            mDisplay.asyncExec(new Runnable() {
                public void run() {
                    doSetStatusLine(str);
                }
            });
        } catch (SWTException swte) {
            if (!mDisplay.isDisposed())
                throw swte;
        }