FileDocCategorySizeDatePackage
ThreadPanel.javaAPI DocAndroid 1.5 API21657Wed May 06 22:41:08 BST 2009com.android.ddmuilib

ThreadPanel

public class ThreadPanel extends TablePanel
Base class for our information panels.

Fields Summary
private static final String
PREFS_THREAD_COL_ID
private static final String
PREFS_THREAD_COL_TID
private static final String
PREFS_THREAD_COL_STATUS
private static final String
PREFS_THREAD_COL_UTIME
private static final String
PREFS_THREAD_COL_STIME
private static final String
PREFS_THREAD_COL_NAME
private static final String
PREFS_THREAD_SASH
private static final String
PREFS_STACK_COL_CLASS
private static final String
PREFS_STACK_COL_METHOD
private static final String
PREFS_STACK_COL_FILE
private static final String
PREFS_STACK_COL_LINE
private static final String
PREFS_STACK_COL_NATIVE
private org.eclipse.swt.widgets.Display
mDisplay
private org.eclipse.swt.widgets.Composite
mBase
private org.eclipse.swt.widgets.Label
mNotEnabled
private org.eclipse.swt.widgets.Label
mNotSelected
private org.eclipse.swt.widgets.Composite
mThreadBase
private org.eclipse.swt.widgets.Table
mThreadTable
private org.eclipse.jface.viewers.TableViewer
mThreadViewer
private org.eclipse.swt.widgets.Composite
mStackTraceBase
private org.eclipse.swt.widgets.Button
mRefreshStackTraceButton
private org.eclipse.swt.widgets.Label
mStackTraceTimeLabel
private StackTracePanel
mStackTracePanel
private org.eclipse.swt.widgets.Table
mStackTraceTable
private boolean
mMustStopRecurringThreadUpdate
Indicates if a timer-based Runnable is current requesting thread updates regularly.
private boolean
mRecurringThreadUpdateRunning
Flag to tell the recurring thread update to stop running
private Object
mLock
private static final String[]
THREAD_STATUS
Constructors Summary
Methods Summary
public voidclientChanged(com.android.ddmlib.Client client, int changeMask)
Sent when an existing client information changed.

This is sent from a non UI thread.

param
client the updated client.
param
changeMask the bit mask describing the changed properties. It can contain any of the following values: {@link Client#CHANGE_INFO}, {@link Client#CHANGE_NAME} {@link Client#CHANGE_DEBUGGER_INTEREST}, {@link Client#CHANGE_THREAD_MODE}, {@link Client#CHANGE_THREAD_DATA}, {@link Client#CHANGE_HEAP_MODE}, {@link Client#CHANGE_HEAP_DATA}, {@link Client#CHANGE_NATIVE_HEAP_DATA}
see
IClientChangeListener#clientChanged(Client, int)

        if (client == getCurrentClient()) {
            if ((changeMask & Client.CHANGE_THREAD_MODE) != 0 ||
                    (changeMask & Client.CHANGE_THREAD_DATA) != 0) {
                try {
                    mThreadTable.getDisplay().asyncExec(new Runnable() {
                        public void run() {
                            clientSelected();
                        }
                    });
                } catch (SWTException e) {
                    // widget is disposed, we do nothing
                }
            } else if ((changeMask & Client.CHANGE_THREAD_STACKTRACE) != 0) {
                try {
                    mThreadTable.getDisplay().asyncExec(new Runnable() {
                        public void run() {
                            updateThreadStackCall();
                        }
                    });
                } catch (SWTException e) {
                    // widget is disposed, we do nothing
                }
            }
        }
    
public voidclientSelected()
Sent when a new client is selected. The new client can be accessed with {@link #getCurrentClient()}.

        if (mThreadTable.isDisposed()) {
            return;
        }

        Client client = getCurrentClient();
        
        mStackTracePanel.setCurrentClient(client);

        if (client != null) {
            if (!client.isThreadUpdateEnabled()) {
                ((StackLayout)mBase.getLayout()).topControl = mNotEnabled;
                mThreadViewer.setInput(null);

                // if we are currently updating the thread, stop doing it.
                mMustStopRecurringThreadUpdate = true;
            } else {
                ((StackLayout)mBase.getLayout()).topControl = mThreadBase;
                mThreadViewer.setInput(client);

                synchronized (mLock) {
                    // if we're not updating we start the process
                    if (mRecurringThreadUpdateRunning == false) {
                        startRecurringThreadUpdate();
                    } else if (mMustStopRecurringThreadUpdate) {
                        // else if there's a runnable that's still going to get called, lets
                        // simply cancel the stop, and keep going
                        mMustStopRecurringThreadUpdate = false;
                    }
                }
            }
        } else {
            ((StackLayout)mBase.getLayout()).topControl = mNotSelected;
            mThreadViewer.setInput(null);
        }

        mBase.layout();
    
protected org.eclipse.swt.widgets.ControlcreateControl(org.eclipse.swt.widgets.Composite parent)
Create our control(s).

        mDisplay = parent.getDisplay();

        final IPreferenceStore store = DdmUiPreferences.getStore();

        mBase = new Composite(parent, SWT.NONE);
        mBase.setLayout(new StackLayout());

        // UI for thread not enabled
        mNotEnabled = new Label(mBase, SWT.CENTER | SWT.WRAP);
        mNotEnabled.setText("Thread updates not enabled for selected client\n"
            + "(use toolbar button to enable)");

        // UI for not client selected
        mNotSelected = new Label(mBase, SWT.CENTER | SWT.WRAP);
        mNotSelected.setText("no client is selected");

        // base composite for selected client with enabled thread update.
        mThreadBase = new Composite(mBase, SWT.NONE);
        mThreadBase.setLayout(new FormLayout());
        
        // table above the sash
        mThreadTable = new Table(mThreadBase, SWT.MULTI | SWT.FULL_SELECTION);
        mThreadTable.setHeaderVisible(true);
        mThreadTable.setLinesVisible(true);

        TableHelper.createTableColumn(
                mThreadTable,
                "ID",
                SWT.RIGHT,
                "888", //$NON-NLS-1$
                PREFS_THREAD_COL_ID, store);

        TableHelper.createTableColumn(
                mThreadTable,
                "Tid",
                SWT.RIGHT,
                "88888", //$NON-NLS-1$
                PREFS_THREAD_COL_TID, store);

        TableHelper.createTableColumn(
                mThreadTable,
                "Status",
                SWT.LEFT,
                "timed-wait", //$NON-NLS-1$
                PREFS_THREAD_COL_STATUS, store);

        TableHelper.createTableColumn(
                mThreadTable,
                "utime",
                SWT.RIGHT,
                "utime", //$NON-NLS-1$
                PREFS_THREAD_COL_UTIME, store);

        TableHelper.createTableColumn(
                mThreadTable,
                "stime",
                SWT.RIGHT,
                "utime", //$NON-NLS-1$
                PREFS_THREAD_COL_STIME, store);

        TableHelper.createTableColumn(
                mThreadTable,
                "Name",
                SWT.LEFT,
                "android.class.ReallyLongClassName.MethodName", //$NON-NLS-1$
                PREFS_THREAD_COL_NAME, store);
        
        mThreadViewer = new TableViewer(mThreadTable);
        mThreadViewer.setContentProvider(new ThreadContentProvider());
        mThreadViewer.setLabelProvider(new ThreadLabelProvider());

        mThreadViewer.addSelectionChangedListener(new ISelectionChangedListener() {
            public void selectionChanged(SelectionChangedEvent event) {
                ThreadInfo selectedThread = getThreadSelection(event.getSelection());
                updateThreadStackTrace(selectedThread);
            }
        });
        mThreadViewer.addDoubleClickListener(new IDoubleClickListener() {
            public void doubleClick(DoubleClickEvent event) {
                ThreadInfo selectedThread = getThreadSelection(event.getSelection());
                if (selectedThread != null) {
                    Client client = (Client)mThreadViewer.getInput();
                    
                    if (client != null) {
                        client.requestThreadStackTrace(selectedThread.getThreadId());
                    }
                }
            }
        });
        
        // the separating sash
        final Sash sash = new Sash(mThreadBase, SWT.HORIZONTAL);
        Color darkGray = parent.getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY);
        sash.setBackground(darkGray);
        
        // the UI below the sash
        mStackTraceBase = new Composite(mThreadBase, SWT.NONE);
        mStackTraceBase.setLayout(new GridLayout(2, false));

        mRefreshStackTraceButton = new Button(mStackTraceBase, SWT.PUSH);
        mRefreshStackTraceButton.setText("Refresh");
        mRefreshStackTraceButton.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                ThreadInfo selectedThread = getThreadSelection(null);
                if (selectedThread != null) {
                    Client currentClient = getCurrentClient();
                    if (currentClient != null) {
                        currentClient.requestThreadStackTrace(selectedThread.getThreadId());
                    }
                }
            }
        });
        
        mStackTraceTimeLabel = new Label(mStackTraceBase, SWT.NONE);
        mStackTraceTimeLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

        mStackTracePanel = new StackTracePanel();
        mStackTraceTable = mStackTracePanel.createPanel(mStackTraceBase,
                PREFS_STACK_COL_CLASS,
                PREFS_STACK_COL_METHOD,
                PREFS_STACK_COL_FILE,
                PREFS_STACK_COL_LINE,
                PREFS_STACK_COL_NATIVE,
                store);
        
        GridData gd;
        mStackTraceTable.setLayoutData(gd = new GridData(GridData.FILL_BOTH));
        gd.horizontalSpan = 2;
        
        // now setup the sash.
        // 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);
        mThreadTable.setLayoutData(data);

        final FormData sashData = new FormData();
        if (store != null && store.contains(PREFS_THREAD_SASH)) {
            sashData.top = new FormAttachment(0, store.getInt(PREFS_THREAD_SASH));
        } 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);
        mStackTraceBase.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 = mThreadBase.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);
                    store.setValue(PREFS_THREAD_SASH, e.y);
                    mThreadBase.layout();
                }
            }
        });

        ((StackLayout)mBase.getLayout()).topControl = mNotSelected;

        return mBase;
    
public voiddeviceSelected()
Sent when a new device is selected. The new device can be accessed with {@link #getCurrentDevice()}.

        // pass
    
private com.android.ddmlib.ThreadInfogetThreadSelection(org.eclipse.jface.viewers.ISelection selection)
Returns the current thread selection or null if none is found. If a {@link ISelection} object is specified, the first {@link ThreadInfo} from this selection is returned, otherwise, the ISelection returned by {@link TableViewer#getSelection()} is used.

param
selection the {@link ISelection} to use, or null

        if (selection == null) {
            selection = mThreadViewer.getSelection();
        }
        
        if (selection instanceof IStructuredSelection) {
            IStructuredSelection structuredSelection = (IStructuredSelection)selection;
            Object object = structuredSelection.getFirstElement();
            if (object instanceof ThreadInfo) {
                return (ThreadInfo)object;
            }
        }
        
        return null;
    
public voidsetFocus()
Sets the focus to the proper control inside the panel.

        mThreadTable.setFocus();
    
protected voidsetTableFocusListener()

        addTableToFocusListener(mThreadTable);
        addTableToFocusListener(mStackTraceTable);
    
private voidstartRecurringThreadUpdate()
Initiate recurring events. We use a shorter "initialWait" so we do the first execution sooner. We don't do it immediately because we want to give the clients a chance to get set up.

        mRecurringThreadUpdateRunning = true;
        int initialWait = 1000;

        mDisplay.timerExec(initialWait, new Runnable() {
            public void run() {
                synchronized (mLock) {
                    // lets check we still want updates.
                    if (mMustStopRecurringThreadUpdate == false) {
                        Client client = getCurrentClient();
                        if (client != null) {
                            client.requestThreadUpdate();

                            mDisplay.timerExec(
                                    DdmUiPreferences.getThreadRefreshInterval() * 1000, this);
                        } else {
                            // we don't have a Client, which means the runnable is not
                            // going to be called through the timer. We reset the running flag.
                            mRecurringThreadUpdateRunning = false;
                        }
                    } else {
                        // else actually stops (don't call the timerExec) and reset the flags.
                        mRecurringThreadUpdateRunning = false;
                        mMustStopRecurringThreadUpdate = false;
                    }
                }
            }
        });
    
private voidupdateThreadStackCall()
Updates the stack call of the currently selected thread.

This must be called from the UI thread.

        Client client = getCurrentClient();
        if (client != null) {
            // get the current selection in the ThreadTable
            ThreadInfo selectedThread = getThreadSelection(null);
            
            if (selectedThread != null) {
                updateThreadStackTrace(selectedThread);
            } else {
                updateThreadStackTrace(null);
            }
        }
    
private voidupdateThreadStackTrace(com.android.ddmlib.ThreadInfo thread)
updates the stackcall of the specified thread. If null the UI is emptied of current data.

param
thread

        mStackTracePanel.setViewerInput(thread);
        
        if (thread != null) {
            mRefreshStackTraceButton.setEnabled(true);
            long stackcallTime = thread.getStackCallTime();
            if (stackcallTime != 0) {
                String label = new Date(stackcallTime).toString();
                mStackTraceTimeLabel.setText(label);
            } else {
                mStackTraceTimeLabel.setText(""); //$NON-NLS-1$
            }
        } else {
            mRefreshStackTraceButton.setEnabled(true);
            mStackTraceTimeLabel.setText(""); //$NON-NLS-1$
        }