com.nokia.carbide.cpp.debug.kernelaware.ui.SymbianOSView.java Source code

Java tutorial

Introduction

Here is the source code for com.nokia.carbide.cpp.debug.kernelaware.ui.SymbianOSView.java

Source

/*
* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). 
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description: 
*
*/
package com.nokia.carbide.cpp.debug.kernelaware.ui;

import org.eclipse.cdt.debug.core.model.ICDebugTarget;
import org.eclipse.cdt.debug.core.model.ICStackFrame;
import org.eclipse.cdt.debug.core.model.ICThread;
import org.eclipse.cdt.debug.internal.core.model.CDebugElement;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.debug.core.DebugEvent;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.IDebugEventSetListener;
import org.eclipse.debug.ui.IDebugUIConstants;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.dialogs.IInputValidator;
import org.eclipse.jface.dialogs.InputDialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredViewer;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.TabFolder;
import org.eclipse.swt.widgets.TabItem;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.INullSelectionListener;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.IViewSite;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchPartReference;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.ViewPart;
import org.eclipse.ui.plugin.AbstractUIPlugin;

import com.freescale.cdt.debug.cw.core.CWPlugin;
import com.freescale.cdt.debug.cw.core.cdi.Session;
import com.nokia.carbide.cpp.debug.kernelaware.IDataChangedListener;
import com.nokia.carbide.cpp.debug.kernelaware.IOSObjectProperties;
import com.nokia.carbide.cpp.debug.kernelaware.KernelawarePlugin;
import com.nokia.carbide.cpp.debug.kernelaware.OSDataManager;
import com.nokia.carbide.cpp.debug.kernelaware.OSObjectProcess;
import com.nokia.carbide.cpp.debug.kernelaware.OSObjectThread;
import com.nokia.carbide.cpp.internal.featureTracker.FeatureUseTrackerConsts;
import com.nokia.carbide.cpp.internal.featureTracker.FeatureUseTrackerPlugin;

import cwdbg.EclipseDEConstants;

public class SymbianOSView extends ViewPart implements ISelectionListener, INullSelectionListener,
        IDataChangedListener, Runnable, IDebugEventSetListener {

    /*
     * Help context ID. Should be: PluginID + "." + <words without '.'>
     */
    private static final String HelpID_Prefix = KernelawarePlugin.getUniqueIdentifier() + "."; //$NON-NLS-1$

    public static final String s_HelpID_View = HelpID_Prefix + "symbian_os_data_view"; //$NON-NLS-1$

    public static final String s_HelpID_OverviewTab = HelpID_Prefix + "symbian_os_data_view_overview_tab"; //$NON-NLS-1$

    public static final String s_HelpID_ProcessTab = HelpID_Prefix + "symbian_os_data_view_process_tab"; //$NON-NLS-1$

    public static final String s_HelpID_ThreadTab = HelpID_Prefix + "symbian_os_data_view_thread_tab"; //$NON-NLS-1$

    public static final String s_HelpID_ChunkTab = HelpID_Prefix + "symbian_os_data_view_chunk_tab"; //$NON-NLS-1$

    public static final String s_HelpID_LibraryTab = HelpID_Prefix + "symbian_os_data_view_library_tab"; //$NON-NLS-1$

    public static final ImageDescriptor clearImageDesc = AbstractUIPlugin
            .imageDescriptorFromPlugin(PlatformUI.PLUGIN_ID, "$nl$/icons/full/etool16/clear_co.gif"); //$NON-NLS-1$

    private TabItem m_overviewTabItem;

    private TabItem m_processTabItem;

    private TabItem m_threadTabItem;

    private TabItem m_chunkTabItem;

    private TabItem m_libraryTabItem;

    private TreeViewer m_overviewTreeViewer;

    private TableViewer m_processTableViewer;

    private TableViewer m_threadTableViewer;

    private TableViewer m_chunkTableViewer;

    private TableViewer m_libraryTableViewer;

    private StructuredViewer m_currentViewer;

    private Session m_currentSession = null;

    private Action m_actionRefresh;

    private Action m_actionToggleAutoRefresh;

    private Action m_actionDebug;

    private Action m_actionProperties;

    private Action m_actionSetTimer;

    private Action m_actionCollapseAll;

    private Action m_actionDoubleClick;

    // in seconds.
    private int m_timedRefreshInterval = 20;

    // Is the view closed by user ?
    private boolean m_disposed;

    // Make this option static so that it's consistent across view sessions.
    static boolean m_autoRefresh = true;

    private TabFolder tabFolder;

    private int refreshCount;

    /*
     * (non-Javadoc)
     * 
     * @see com.freescale.cdt.debug.cw.core.ui.os.OSView#createPartControl(org.eclipse.swt.widgets.Composite)
     */
    @Override
    public void createPartControl(Composite parent) {

        tabFolder = new TabFolder(parent, SWT.NONE);
        tabFolder.addSelectionListener(new SelectionAdapter() {
            public void widgetSelected(SelectionEvent e) {
                DoAction_TabSelection(e.item);
            }
        });

        ImageDescriptor imageDesc;

        /***********************************************************************
         * Overview (treeview) tab
         */
        m_overviewTabItem = new TabItem(tabFolder, SWT.NONE);
        m_overviewTabItem.setToolTipText(Messages.getString("SymbianOSView.OverViewToolTip")); //$NON-NLS-1$
        m_overviewTabItem.setText(Messages.getString("SymbianOSView.OverViewLabel")); //$NON-NLS-1$
        imageDesc = KernelawarePlugin.getImageDescriptor("icons//tab_overview.png"); //$NON-NLS-1$
        m_overviewTabItem.setImage(imageDesc.createImage());

        m_overviewTreeViewer = (new OverviewTab()).createControl(tabFolder, m_overviewTabItem);

        /***********************************************************************
         * Process Tab
         */
        m_processTabItem = new TabItem(tabFolder, SWT.NONE);
        m_processTabItem.setToolTipText(Messages.getString("SymbianOSView.ProcessesToolTip")); //$NON-NLS-1$
        m_processTabItem.setText(Messages.getString("SymbianOSView.ProcessesLabel")); //$NON-NLS-1$
        imageDesc = KernelawarePlugin.getImageDescriptor("icons//tab_process.gif"); //$NON-NLS-1$
        m_processTabItem.setImage(imageDesc.createImage());

        m_processTableViewer = (new ProcessTab()).createControl(tabFolder, m_processTabItem);

        /***********************************************************************
         * Thread Tab
         */
        m_threadTabItem = new TabItem(tabFolder, SWT.NONE);
        m_threadTabItem.setToolTipText(Messages.getString("SymbianOSView.ThreadsToolTip")); //$NON-NLS-1$
        m_threadTabItem.setText(Messages.getString("SymbianOSView.ThreadsLabel")); //$NON-NLS-1$
        imageDesc = KernelawarePlugin.getImageDescriptor("icons//tab_thread.gif"); //$NON-NLS-1$
        m_threadTabItem.setImage(imageDesc.createImage());

        m_threadTableViewer = (new ThreadTab()).createControl(tabFolder, m_threadTabItem);

        /***********************************************************************
         * Chunk Tab
         */
        m_chunkTabItem = new TabItem(tabFolder, SWT.NONE);
        m_chunkTabItem.setToolTipText(Messages.getString("SymbianOSView.ChunksToolTip")); //$NON-NLS-1$
        m_chunkTabItem.setText(Messages.getString("SymbianOSView.ChunksLabel")); //$NON-NLS-1$
        imageDesc = KernelawarePlugin.getImageDescriptor("icons//tab_chunk.gif"); //$NON-NLS-1$
        m_chunkTabItem.setImage(imageDesc.createImage());

        m_chunkTableViewer = (new ChunkTab()).createControl(tabFolder, m_chunkTabItem);

        /***********************************************************************
         * Library Tab
         */
        m_libraryTabItem = new TabItem(tabFolder, SWT.NONE);
        m_libraryTabItem.setToolTipText(Messages.getString("SymbianOSView.LibrariesToolTip")); //$NON-NLS-1$
        m_libraryTabItem.setText(Messages.getString("SymbianOSView.LibrariesLabel")); //$NON-NLS-1$
        imageDesc = KernelawarePlugin.getImageDescriptor("icons//tab_library.gif"); //$NON-NLS-1$
        m_libraryTabItem.setImage(imageDesc.createImage());

        m_libraryTableViewer = (new LibraryTab()).createControl(tabFolder, m_libraryTabItem);

        /***********************************************************************
         * Other View setting
         */
        // Default current tab.
        tabFolder.setSelection(0);

        // Default selection provider.
        // add a viewer as selection provider of the view, so that selected item
        // in the viewer can have its properties displayed in Properties View.
        m_currentViewer = m_overviewTreeViewer;
        getSite().setSelectionProvider(m_currentViewer);

        // listen to selection in debug view
        getSite().getPage().addSelectionListener(IDebugUIConstants.ID_DEBUG_VIEW, this);

        /***********************************************************************
         * Help context IDs.
         */
        PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, s_HelpID_View);
        PlatformUI.getWorkbench().getHelpSystem().setHelp(m_overviewTabItem.getControl(), s_HelpID_OverviewTab);
        PlatformUI.getWorkbench().getHelpSystem().setHelp(m_processTabItem.getControl(), s_HelpID_ProcessTab);
        PlatformUI.getWorkbench().getHelpSystem().setHelp(m_threadTabItem.getControl(), s_HelpID_ThreadTab);
        PlatformUI.getWorkbench().getHelpSystem().setHelp(m_chunkTabItem.getControl(), s_HelpID_ChunkTab);
        PlatformUI.getWorkbench().getHelpSystem().setHelp(m_libraryTabItem.getControl(), s_HelpID_LibraryTab);

        /***********************************************************************
         * Actions
         */
        makeActions();
        hookContextMenu();
        hookDoubleClickAction();
        contributeToActionBars();

        hookSelectionChangedListeners();

        m_disposed = false;

        // If there is already a debug session running, try to show OS data for
        // it.
        Session currSelectedSession = Session.getUIFocusSession();
        if (currSelectedSession != null) {
            // getUIFocusSession() above may give us a dead session. Excluse
            // that case.
            if (currSelectedSession.isActive()) {
                try {
                    showDataForDevice(currSelectedSession);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        updateActionStatus();

        DebugPlugin.getDefault().addDebugEventListener(this);
    }

    public void dataDirty() {
        // Must run this in UI thread.
        PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
            public void run() {
                showDataAsStale(true);
            }
        });
    };

    private void hookContextMenu() {
        MenuManager menuMgr = new MenuManager("#PopupMenu"); //$NON-NLS-1$
        menuMgr.setRemoveAllWhenShown(true);
        menuMgr.addMenuListener(new IMenuListener() {
            public void menuAboutToShow(IMenuManager manager) {
                SymbianOSView.this.fillContextMenu(manager);
            }
        });

        // Hook up context menu for each of the viewers.
        //
        Menu menu;
        StructuredViewer viewer = m_overviewTreeViewer;
        menu = menuMgr.createContextMenu(viewer.getControl());
        viewer.getControl().setMenu(menu);
        getSite().registerContextMenu(menuMgr, viewer);

        viewer = m_processTableViewer;
        menu = menuMgr.createContextMenu(viewer.getControl());
        viewer.getControl().setMenu(menu);
        getSite().registerContextMenu(menuMgr, viewer);

        viewer = m_threadTableViewer;
        menu = menuMgr.createContextMenu(viewer.getControl());
        viewer.getControl().setMenu(menu);
        getSite().registerContextMenu(menuMgr, viewer);

        viewer = m_chunkTableViewer;
        menu = menuMgr.createContextMenu(viewer.getControl());
        viewer.getControl().setMenu(menu);
        getSite().registerContextMenu(menuMgr, viewer);

        viewer = m_libraryTableViewer;
        menu = menuMgr.createContextMenu(viewer.getControl());
        viewer.getControl().setMenu(menu);
        getSite().registerContextMenu(menuMgr, viewer);
    }

    /**
     * Hook up listeners for selection change in the view.
     */
    private void hookSelectionChangedListeners() {
        ISelectionChangedListener listener = new ISelectionChangedListener() {
            public void selectionChanged(SelectionChangedEvent event) {
                // Update status of commands.
                updateActionStatus();
            }
        };

        m_overviewTreeViewer.addSelectionChangedListener(listener);
        m_processTableViewer.addSelectionChangedListener(listener);
        m_threadTableViewer.addSelectionChangedListener(listener);
        m_chunkTableViewer.addSelectionChangedListener(listener);
        m_libraryTableViewer.addSelectionChangedListener(listener);
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.ui.part.ViewPart#init(org.eclipse.ui.IViewSite,
     *      org.eclipse.ui.IMemento)
     */
    @Override
    public void init(IViewSite site, IMemento memento) throws PartInitException {
        super.init(site, memento);
        FeatureUseTrackerPlugin.getFeatureUseProxy().startUsingFeature(FeatureUseTrackerConsts.CARBIDE_KERNELAWARE);
    }

    @Override
    public void dispose() {
        // Must do the following, otherwise a zombie will exist after user close
        // the view.
        //
        m_disposed = true;

        getSite().setSelectionProvider(null);

        getSite().getPage().removeSelectionListener(IDebugUIConstants.ID_DEBUG_VIEW, this);

        DebugPlugin.getDefault().removeDebugEventListener(this);

        // remove the timed task.
        disableTimedRefresh();

        FeatureUseTrackerPlugin.getFeatureUseProxy().stopUsingFeature(FeatureUseTrackerConsts.CARBIDE_KERNELAWARE);

        super.dispose();
    }

    @Override
    public void setFocus() {
        // Make sure the current tab get the focus so that help system can
        // get the correct context ID for the focused control so as to display
        // matching context-sensitive help......12/18/06
        m_currentViewer.getControl().setFocus();
    }

    // This is called when selection in workbench changes.
    // We only honor the change in Debug View.
    public void selectionChanged(IWorkbenchPart part, ISelection selection) {
        if (!(selection instanceof IStructuredSelection))
            return;

        IStructuredSelection structSel = (IStructuredSelection) selection;
        if (structSel.size() != 1)
            return;

        Object sel = structSel.getFirstElement();

        // Allow StackFrame & thread for now.
        // If we hornor selection of CDebugTarget, debugger may freeze on start
        // when the OS View is open before debugger starts.
        // Figure out a solution later..... 11/29/06
        // Not a problem any more in Eclipse 3.3 + CDT4.0....... 11/15/07
        //
        if (!(sel instanceof ICStackFrame || sel instanceof ICThread || sel instanceof ICDebugTarget))
            return;

        Session newSession = Session.getSessionFromDebugContext(sel);

        if (newSession == null) // how come ?
            return;

        /* A terminated session, or a special case:
         * Start the first debug session while the OS View is open, the
         * CDT Target (process) is created and auto selected in Debug View
         * but corresponding CWProcess is not created yet. In such case
         * we should not try to refresh. Part of fix to bug 5320.. 12/04/07
         */
        if (!newSession.isActive())
            return;

        /*
         * If user just selects a different item (stackframe, thread, process)
         * in the same session, and the session's OS data is not dirty, don't
         * bother to update.
         */
        boolean refresh = false;

        if (m_currentSession == null) {
            refresh = true;
        } else if (!m_currentSession.equals(newSession))
            // Different session is selected (by user).
            refresh = true;
        else {
            // The same session is selected (by user or automatically done when debuggee
            // is suspended). Refresh only when data is dirty and AutoRefresh is
            // turned on.
            if (newSession.getOSDataManager().isDataDirty() && m_autoRefresh) {
                refresh = true;

                // Check this for stop mode debug. Fix bug 3467...03/28/07
                if (newSession.isSystemModeDebug() && newSession.isSystemRunning())
                    refresh = false;
            }
        }

        if (refresh) {
            try {
                showDataForDevice(newSession);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * Asynchronously get the OS data for the given session. Show progress and
     * allow user cancel.
     * 
     * @param session -
     *            for which session (i.e. machine) to get the OS data.
     * @param listener -
     *            who's interested in finish event.
     * @param userInitiated -
     *            is the compute initiated by user or automatically by program.
     */
    private void computeInput(final Session session, final IDataChangedListener listener,
            final boolean userInitiated) {

        // Note the job name is also referenced in Target.java.
        // So watch out when changing it !....04/07/07
        Job refreshJob = new Job(Messages.getString("SymbianOSView.RefreshDataTitle")) { //$NON-NLS-1$
            protected IStatus run(IProgressMonitor monitor) {
                IStatus status = Status.OK_STATUS;
                try {
                    OSDataManager osDM = (OSDataManager) session.getOSDataManager();

                    if (osDM != null) {
                        /*
                         * For crash debugger, the refreshing is time consuming
                         * (more than one minute at present). So never do
                         * auto-refresh for it, only user-initiated refreshing
                         * is allowed. This way startup of crash debugger won't
                         * be slow.... [LW_20070213] 02/13/07
                         */
                        if (!(session.getLaunchTypeName().contains("crash") && !userInitiated)) //$NON-NLS-1$
                            status = osDM.updateAll(monitor);
                    }
                } catch (DebugException e) {
                    status = new Status(IStatus.ERROR, KernelawarePlugin.getUniqueIdentifier(), 0, e.getMessage(),
                            null);
                }

                listener.dataUpdated(session, status);
                refreshCount++;

                monitor.done();

                return status;
            }
        };

        refreshJob.setUser(userInitiated);
        refreshJob.setPriority(Job.LONG);
        refreshJob.schedule();
    }

    /**
     * Given a session (namely a machine, or device), display its OS data in the
     * view.
     * 
     * @param newSession
     */
    private void showDataForDevice(Session newSession) throws InterruptedException {
        // Make the view be listener for dataChanged event from the underlying
        // session.
        if (m_currentSession != null)
            ((OSDataManager) m_currentSession.getOSDataManager()).removeDataChangedListener(this);

        ((OSDataManager) newSession.getOSDataManager()).addDataChangedListener(this);

        // User selects a debug session of ours.
        m_currentSession = newSession;

        // This fixes the timing issue with attaching to a process @bug 7580 
        Thread.sleep(500);

        updateActionStatus();

        // if the new session is for stop mode, disable timer for auto-refresh.
        // ...... 12/03/06
        if (newSession.isSystemModeDebug()) {
            // Stop the timed task.
            disableTimedRefresh();
        }
        // The scheduled refresh may conflict with the computeInput() 
        // below and result in deadlock. 
        // Actually the timed refresh will be scheduled in computeInput().
        // So don't do this here......... 12/04/2007 
        //      else if (newSession.isActive())   // Don't if the session is a terminated one 
        //         scheduleTimedRefresh();

        showDataAsStale(true);

        computeInput(m_currentSession, this, false);

        /*
         * Hide Chunk and Library tabs per debug session type. But I don't see
         * an easy way in SWT yet.........Nov. 2006
         */
    }

    private void packTableViewer(TableViewer viewer) {
        TableColumn[] cols = viewer.getTable().getColumns();
        for (int i = 0; i < cols.length; i++)
            if (cols[i].getResizable() != false)
                cols[i].pack();
    }

    private void contributeToActionBars() {
        IActionBars bars = getViewSite().getActionBars();
        fillLocalPullDown(bars.getMenuManager());
        fillLocalToolBar(bars.getToolBarManager());
    }

    private void fillContextMenu(IMenuManager manager) {
        if (m_currentViewer == null)
            return;

        ISelection selection = m_currentViewer.getSelection();
        if (selection == null)
            return;

        Object obj = ((IStructuredSelection) selection).getFirstElement();
        if (obj == null)
            return;

        // Add "debug" command if selection is a process or thread.
        if (obj instanceof OSObjectProcess || obj instanceof OSObjectThread) {
            manager.add(m_actionDebug);
        }

        // For all others, add "properties" command.
        manager.add(m_actionProperties);

        // Other plug-ins can contribute their actions here
        manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
    }

    private void fillLocalPullDown(IMenuManager manager) {
        manager.add(m_actionRefresh);
        manager.add(m_actionToggleAutoRefresh);
        manager.add(m_actionDebug);
        manager.add(new Separator());
        manager.add(m_actionProperties);
        manager.add(m_actionSetTimer);
        manager.add(m_actionCollapseAll);
    }

    private void fillLocalToolBar(IToolBarManager manager) {
        manager.add(m_actionRefresh);
        manager.add(m_actionToggleAutoRefresh);
        manager.add(m_actionDebug);
        manager.add(m_actionProperties);
        manager.add(m_actionSetTimer);
        manager.add(m_actionCollapseAll);
    }

    private void makeActions() {
        m_actionRefresh = new Action(Messages.getString("SymbianOSView.RefreshActionLabel"), //$NON-NLS-1$
                IAction.AS_PUSH_BUTTON) {
            public void run() {
                DoAction_Refresh();
            }
        };
        m_actionRefresh.setToolTipText(Messages.getString("SymbianOSView.RefreshActionToolTip")); //$NON-NLS-1$
        m_actionRefresh.setImageDescriptor(KernelawarePlugin.getImageDescriptor("icons//refresh.gif")); //$NON-NLS-1$

        m_actionToggleAutoRefresh = new Action(Messages.getString("SymbianOSView.AutoRefreshActionLabel"), //$NON-NLS-1$
                IAction.AS_CHECK_BOX) {
            public void run() {
                DoAction_ToggleAutoRefresh();
            }
        };
        m_actionToggleAutoRefresh.setToolTipText(Messages.getString("SymbianOSView.AutoRefreshActionToolTip")); //$NON-NLS-1$
        m_actionToggleAutoRefresh.setChecked(m_autoRefresh);
        m_actionToggleAutoRefresh
                .setImageDescriptor(KernelawarePlugin.getImageDescriptor("icons//auto_refresh.gif")); //$NON-NLS-1$

        m_actionDebug = new Action(Messages.getString("SymbianOSView.DebugActionLabel"), IAction.AS_PUSH_BUTTON) { //$NON-NLS-1$
            public void run() {
                DoAction_Debug();
            }
        };
        m_actionDebug.setToolTipText(Messages.getString("SymbianOSView.DebugActionToolTip")); //$NON-NLS-1$
        m_actionDebug.setImageDescriptor(KernelawarePlugin.getImageDescriptor("icons//Debug.png")); //$NON-NLS-1$

        m_actionProperties = new Action(Messages.getString("SymbianOSView.PropertiesActionLabel"), //$NON-NLS-1$
                IAction.AS_PUSH_BUTTON) {
            public void run() {
                DoAction_Properties();
            }
        };
        m_actionProperties.setToolTipText(Messages.getString("SymbianOSView.PropertiesActionToolTip")); //$NON-NLS-1$
        m_actionProperties.setImageDescriptor(KernelawarePlugin.getImageDescriptor("icons//properties.gif")); //$NON-NLS-1$

        m_actionSetTimer = new Action(Messages.getString("SymbianOSView.SetTimerActionLabel"), //$NON-NLS-1$
                IAction.AS_PUSH_BUTTON) {
            public void run() {
                DoAction_SetTimer();
            }
        };
        m_actionSetTimer.setToolTipText(Messages.getString("SymbianOSView.SetTimerActionToolTip")); //$NON-NLS-1$
        m_actionSetTimer.setImageDescriptor(KernelawarePlugin.getImageDescriptor("icons//set_timer.gif")); //$NON-NLS-1$

        m_actionCollapseAll = new Action(Messages.getString("SymbianOSView.CollapseAllActionLabel"), //$NON-NLS-1$
                IAction.AS_PUSH_BUTTON) {
            public void run() {
                DoAction_CollapseAll();
            }
        };
        m_actionCollapseAll.setToolTipText(Messages.getString("SymbianOSView.CollapseAllActionToolTip")); //$NON-NLS-1$
        m_actionCollapseAll.setImageDescriptor(KernelawarePlugin.getImageDescriptor("icons//collapseall.gif")); //$NON-NLS-1$

        m_actionDoubleClick = new Action() {
            public void run() {
                DoAction_Debug();
            }
        };
    }

    private void hookDoubleClickAction() {
        IDoubleClickListener listener = new IDoubleClickListener() {
            public void doubleClick(DoubleClickEvent event) {
                m_actionDoubleClick.run();
            }
        };

        m_processTableViewer.addDoubleClickListener(listener);
        m_threadTableViewer.addDoubleClickListener(listener);
    }

    private void showMessage(String message) {
        MessageDialog.openInformation(m_processTableViewer.getControl().getShell(),
                Messages.getString("SymbianOSView.MessageTitle"), //$NON-NLS-1$
                Messages.getString("SymbianOSView.MessagePrefix") + message); //$NON-NLS-1$
    }

    public void DoAction_TabSelection(Widget item) {
        if (item == null)
            return;

        if (item.equals(m_overviewTabItem)) {
            m_currentViewer = m_overviewTreeViewer;
        } else if (item.equals(m_processTabItem))
            m_currentViewer = m_processTableViewer;
        else if (item.equals(m_threadTabItem))
            m_currentViewer = m_threadTableViewer;
        else if (item.equals(m_chunkTabItem))
            m_currentViewer = m_chunkTableViewer;
        else if (item.equals(m_libraryTabItem))
            m_currentViewer = m_libraryTableViewer;

        getSite().setSelectionProvider(m_currentViewer);

        /*
         * Now the tab selection has changed.
         * 
         * At this time if user selects an item in the tab, the properties of
         * the item won't be displayed in Properties View. That's because the
         * above selectionProvider change is not honored until the view loses
         * and regains focus (a platform bug ?). So we do this: change focus to
         * any other view (here I choose Variable View as it's most likely
         * present and in the same pane as this OS View) and then change it back
         * to this view. This is not a perfect solution, but...
         * 
         * 
         * This is also needed for the help system to get tab-specific context
         * ID. See setFocus() for more.
         */
        int viewState_old = IWorkbenchPage.STATE_RESTORED;
        int viewState_new = IWorkbenchPage.STATE_RESTORED;
        IWorkbenchPartReference thisRef = getSite().getPage().getReference(this);
        if (thisRef != null)
            viewState_old = getSite().getPage().getPartState(thisRef);

        try {
            getSite().getPage().showView(IDebugUIConstants.ID_VARIABLE_VIEW);
        } catch (PartInitException e) {
            e.printStackTrace();
        }

        if (thisRef != null)
            viewState_new = getSite().getPage().getPartState(thisRef);

        // Put focus back.
        getSite().getPage().activate(this);

        // Restore the state.
        // Added the "new != old" check otherwise the setPartState() would cause
        // NPE in core code in Eclipse 3.3
        if (thisRef != null && viewState_new != viewState_old)
            getSite().getPage().setPartState(thisRef, viewState_old);

        // enable/disable actions/commands accordingly.
        updateActionStatus();
    }

    /**
     * Force refresh of all data in the view by re-reading from the target
     * device.
     * 
     */
    public void DoAction_Refresh() {
        if (m_currentSession == null)
            return;

        disableTimedRefresh();

        m_currentSession.getOSDataManager().setDataDirty();

        computeInput(m_currentSession, this, true);
    }

    public void DoAction_ToggleAutoRefresh() {
        setAutoRefresh(!m_autoRefresh);
    }

    public void setAutoRefresh(boolean enabled) {
        m_autoRefresh = enabled;
    }

    public void DoAction_CollapseAll() {
        if (m_overviewTreeViewer != null)
            m_overviewTreeViewer.collapseAll();
    }

    public void DoAction_Debug() {
        /*
         * Attach debugger to a process or thread.
         */
        if (m_currentViewer == null)
            return;
        if (m_currentSession == null || !m_currentSession.isActive())
            return;

        ISelection selection = m_currentViewer.getSelection();
        if (selection == null)
            return;

        Object obj = ((IStructuredSelection) selection).getFirstElement();
        if (obj == null)
            return;

        // Only allow attaching to a process
        int procID, threadID;

        String procName = ""; //$NON-NLS-1$
        if (obj instanceof OSObjectProcess) {

            // User select to debug a process
            OSObjectProcess objProcess = (OSObjectProcess) obj;
            procID = objProcess.getID();
            if (m_currentSession.processBeingDebugged(procID)) {
                showMessage(Messages.getString("SymbianOSView.ProcessUnderDebugMsg")); //$NON-NLS-1$
                return;
            }

            procName = objProcess.getName();

            // Just targetting the first thread in the process
            OSObjectThread[] threads = objProcess.getThreads();
            if (threads.length > 0)
                threadID = threads[0].getID();
            else
                // no thread, should not happen.
                return;
        } else if (obj instanceof OSObjectThread) {

            OSObjectThread thread = (OSObjectThread) obj;
            procID = thread.getProcessID();
            if (procID == EclipseDEConstants.J_OSObjectID_Invalid) // owner
                // unknown
                return;
            threadID = thread.getID();

            if (m_currentSession.threadBeingDebugged(procID, threadID)) {
                showMessage(Messages.getString("SymbianOSView.ThreadUnderDebugMsg")); //$NON-NLS-1$
                return;
            }

            if (m_currentSession.processBeingDebugged(procID)) {
                // the owner process is already under debug
                // no need to pass process name to backend.
                procName = ""; //$NON-NLS-1$
            } else {
                procName = (String) thread.getRawPropertyValue(IOSObjectProperties.ID.OwningProcessName);
            }
        } else
            // other objects
            return;

        // With TRK, we may get process name like "app[ef1a9bef]0001".
        // Do some adjustment here.
        if (procName.length() > 0) {
            int i = procName.indexOf('[');
            if (i > 0)
                procName = procName.substring(0, i);

            // for a name without extension like "app", add .exe
            if (!procName.contains(".")) //$NON-NLS-N$ //$NON-NLS-1$
                procName = procName + ".exe"; //$NON-NLS-N$ //$NON-NLS-1$
        }

        // Now the real action...
        try {
            m_currentSession.attachToThread(procID, threadID, procName);
        } catch (DebugException e) {
            showMessage(e.getMessage());
        }
    }

    /**
     * Open Properties View and show property of selected item in current tab.
     */
    private void DoAction_Properties() {
        if (m_currentViewer == null)
            return;

        ISelection selection = m_currentViewer.getSelection();
        if (selection == null)
            return;

        // Open the Properties view in case it's not.
        try {
            getSite().getPage().showView(org.eclipse.ui.IPageLayout.ID_PROP_SHEET);
        } catch (PartInitException e) {
            e.printStackTrace();
        }

        // Change focus back to this view so that the selection in the viewer is
        // displayed in the Properties View.
        getSite().getPage().activate(this);
    }

    /**
     * Pop up a dialog for user to enter the time interval.
     */
    private void DoAction_SetTimer() {
        // validator for user input.
        class Validator implements IInputValidator {

            public String isValid(String newText) {
                int i;

                try {
                    i = Integer.valueOf(newText);
                } catch (Exception e) {
                    return Messages.getString("SymbianOSView.DecimalError"); //$NON-NLS-1$
                }

                if (i < 3 || i > 600)
                    return Messages.getString("SymbianOSView.RangeError"); //$NON-NLS-1$

                return null;
            }
        }

        int currentInterval = CWPlugin.getDefault().getPluginPreferences()
                .getInt(cwdbg.PreferenceConstants.J_PN_OSViewAutoRefreshInterval);

        InputDialog dg = new InputDialog(this.getSite().getShell(),
                Messages.getString("SymbianOSView.MessageTitle"), // $NON-NLS-1$ //$NON-NLS-1$
                Messages.getString("SymbianOSView.IntervalMessage"), //$NON-NLS-1$
                Integer.toString(currentInterval), new Validator());

        if (dg.open() == Window.OK) {
            String input = dg.getValue();
            currentInterval = Integer.valueOf(input);

            // Save in our global pref storage.
            CWPlugin.getDefault().getPluginPreferences()
                    .setValue(cwdbg.PreferenceConstants.J_PN_OSViewAutoRefreshInterval, currentInterval);
        }
    }

    /**
     * Enable/disable actions based on current selection.
     */
    private void updateActionStatus() {
        boolean enableRefresh = false;
        boolean enableToggleAutoRefresh = true;
        boolean enableDebug = false;
        boolean enableProperties = false;
        boolean enableSetTimer = false;
        boolean enableCollapseAll = false;

        if (m_actionRefresh == null) // not created yet.
            return;

        if (m_currentSession != null && m_currentSession.isActive()) {
            enableRefresh = true;

            if (!m_currentSession.isSystemModeDebug())
                // enable this for TRK debugger only.
                enableSetTimer = true;

            if (m_currentSession.getLaunchTypeName().contains("crash")) //$NON-NLS-1$
                // disable auto-refresh for crash debugger. See my comment
                // [LW_20070213].
                enableToggleAutoRefresh = false;

            if (m_currentViewer != null) {

                // tab specific commands
                //
                if (m_currentViewer == m_overviewTreeViewer)
                    enableCollapseAll = true;

                // Selection specific commands
                //
                ISelection selection = m_currentViewer.getSelection();
                Object obj = ((IStructuredSelection) selection).getFirstElement();
                if (obj != null) {
                    // This is true as long as there is a valid selection in the
                    // view.
                    enableProperties = true;

                    if (obj instanceof OSObjectProcess || obj instanceof OSObjectThread)
                        enableDebug = true;

                }
            }
        }

        m_actionRefresh.setEnabled(enableRefresh);
        m_actionToggleAutoRefresh.setEnabled(enableToggleAutoRefresh);
        m_actionDebug.setEnabled(enableDebug);
        m_actionProperties.setEnabled(enableProperties);
        m_actionSetTimer.setEnabled(enableSetTimer);
        m_actionCollapseAll.setEnabled(enableCollapseAll);
    }

    /**
     * Give visual hint on whether the data in the view is stale.
     * 
     * @param stale
     */
    private void showDataAsStale(boolean stale) {
        if (m_disposed)
            return;

        Color co = stale ? PlatformUI.getWorkbench().getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY) : null;

        /*
         * To show that data is stale, I display them in gray text. But the
         * grayed out text is kinda hard to read. Another solution is to change
         * the background to gray, but not ideal either..... 11/30/06
         */
        // m_overviewTreeViewer.getControl().setBackground(co);
        m_overviewTreeViewer.getControl().setForeground(co);
        m_processTableViewer.getControl().setForeground(co);
        m_threadTableViewer.getControl().setForeground(co);
        m_chunkTableViewer.getControl().setForeground(co);
        m_libraryTableViewer.getControl().setForeground(co);
    }

    private void scheduleTimedRefresh() {
        if (m_currentSession == null || !m_currentSession.isActive())
            return;

        // Not for stop mode debug.
        if (m_currentSession.isSystemModeDebug())
            return;

        // Honor preference settings real time.
        m_timedRefreshInterval = CWPlugin.getDefault().getPluginPreferences()
                .getInt(cwdbg.PreferenceConstants.J_PN_OSViewAutoRefreshInterval);

        Runnable timedRunnable = this;

        PlatformUI.getWorkbench().getDisplay().timerExec(m_timedRefreshInterval * 1000 /* milliseconds */,
                timedRunnable);

    }

    private void disableTimedRefresh() {
        Runnable timedRunnable = this;

        PlatformUI.getWorkbench().getDisplay().timerExec(-1, timedRunnable);
    }

    /**
     * This is for timed auto-refresh of OS data in the view. Refer to where
     * Display.timerExec() is invoked.
     * 
     * @see java.lang.Runable#run
     */
    public void run() {
        // View is closed or session terminated
        if (m_disposed || m_currentSession == null)
            return;

        // show as stale.
        showDataAsStale(true);

        // just in case this is cleared
        if (m_currentSession == null)
            return;

        // Refresh data if so requested.
        if (m_autoRefresh) {
            // Mark cache as dirty.
            m_currentSession.getOSDataManager().setDataDirty();

            computeInput(m_currentSession, this, false);
        } else
            scheduleTimedRefresh();
    }

    public void dataUpdated(final Session session, final IStatus status) {
        // User already selected another session during refreshing.
        // Just do nothing. "selectionChanged()" will make sure data for the new
        // session will be displayed.
        if (session != m_currentSession) {
            return;
        }

        final OSDataManager osDM = (OSDataManager) session.getOSDataManager();
        if (osDM == null)
            return;

        /*
         * Even if we got error, we treat the data as updated so that no more
         * refreshing request will be handled until next auto-refresh or
         * user-refresh. This way user won't get repeated error message when he,
         * say, clicks on different target/thread of the same session in the debug view.
         */
        osDM.setUpdatedFlag(0xFFFF);

        Runnable runnable = new Runnable() {
            public void run() {
                if (status.isOK()) {
                    // Remember tree state
                    Object[] expandedNodes = m_overviewTreeViewer.getExpandedElements();
                    ISelection selection = m_overviewTreeViewer.getSelection();

                    m_overviewTreeViewer.setInput(osDM);

                    // Restore tree state
                    m_overviewTreeViewer.setExpandedElements(expandedNodes);
                    m_overviewTreeViewer.setSelection(selection);
                    m_overviewTreeViewer.getTree().showSelection();

                    m_processTableViewer.setInput(osDM);
                    m_threadTableViewer.setInput(osDM);
                    m_chunkTableViewer.setInput(osDM);
                    m_libraryTableViewer.setInput(osDM);

                    /*
                     * The above call will populate the table. Here we can make
                     * sure each column is packed to show all info in preferred
                     * size. Do we really want to do this as it may be against
                     * user's desire ? Let's wait for user feedback....11/02/06
                     */
                    packTableViewer(m_processTableViewer);
                    packTableViewer(m_threadTableViewer);

                    showDataAsStale(false);
                }

                // Data refreshed, reschedule our next timed refresh.
                scheduleTimedRefresh();

                if (status.getSeverity() != IStatus.OK && status.getSeverity() != IStatus.CANCEL) {
                    showDataAsStale(true);
                    // Now the computeInput() is done in a job, and job
                    // mechanism will display any error automatically.
                }
            }
        };

        // Don't update UI if the view is already closed by user.
        if (!m_disposed)
            PlatformUI.getWorkbench().getDisplay().asyncExec(runnable);
    }

    public void handleDebugEvents(DebugEvent[] events) {
        for (int i = 0; i < events.length; i++) {
            DebugEvent event = events[i];

            if (!(event.getSource() instanceof CDebugElement))
                continue;

            CDebugElement elem = (CDebugElement) event.getSource();
            if (elem == null || !(elem.getCDISession() instanceof Session)) // not from our debugger
                return;

            final Session session = (Session) elem.getCDISession();
            if (session == null)
                return;

            if (event.getKind() == DebugEvent.TERMINATE && elem instanceof ICDebugTarget) {
                // a Target (process) is terminated
                if (session == m_currentSession && !session.isActive()) {
                    // and the session is terminated.

                    session.getOSDataManager().setDataDirty();

                    updateActionStatus();

                    // remove timed task, if any.
                    PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() {
                        public void run() {
                            disableTimedRefresh();

                            // Show data as stale after end of session.
                            showDataAsStale(true);
                        }
                    });

                    m_currentSession = null;
                }
            } else if (event.getKind() == DebugEvent.CREATE && elem instanceof ICThread) {
                // a thread in our debug session is created.
                if (m_currentSession == null) {
                    // If the OS Data view has no associated session yet, 
                    // associate it with the session so that OS Data View can
                    // show data for a never-stopped debug session. Fix bug 4428.
                    // ...... 12/04/07
                    // But only do that for run mode debug. For stop mode, 
                    // we should only show data whenever the system is stopped
                    // and the thread in Debug View is selected...12/05/07 
                    if (session.isSystemModeDebug())
                        return;

                    PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() {
                        public void run() {
                            try {
                                showDataForDevice(session);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    });
                }
            }
        }
    }

    @Override
    public Object getAdapter(Class adapter) {
        if (adapter.isInstance(this))
            return this;
        if (adapter.isInstance(tabFolder))
            return tabFolder;
        if (adapter.isInstance(m_currentViewer))
            return m_currentViewer;

        return super.getAdapter(adapter);
    }

    public int getRefreshCount() {
        return refreshCount;
    }
}