com.nokia.carbide.cpp.pi.address.AddrBinaryTable.java Source code

Java tutorial

Introduction

Here is the source code for com.nokia.carbide.cpp.pi.address.AddrBinaryTable.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.pi.address;

import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.awt.event.FocusEvent;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.Vector;

import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.CheckboxTableViewer;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.Viewer;
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.graphics.Image;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Decorations;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Sash;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;

import com.nokia.carbide.cpp.internal.pi.address.GppModelAdapter;
import com.nokia.carbide.cpp.internal.pi.analyser.NpiInstanceRepository;
import com.nokia.carbide.cpp.internal.pi.analyser.ProfileVisualiser;
import com.nokia.carbide.cpp.internal.pi.model.ProfiledBinary;
import com.nokia.carbide.cpp.internal.pi.model.ProfiledGeneric;
import com.nokia.carbide.cpp.internal.pi.model.ProfiledThreshold;
import com.nokia.carbide.cpp.internal.pi.visual.Defines;
import com.nokia.carbide.cpp.internal.pi.visual.PIEvent;
import com.nokia.carbide.cpp.internal.pi.visual.PIVisualSharedData;
import com.nokia.carbide.cpp.pi.editors.PIPageEditor;
import com.nokia.carbide.cpp.pi.util.TableColorPalette;

public class AddrBinaryTable extends GenericAddrTable {
    // local copy of profiled binaries, which we can sort
    // without affecting the original
    Vector<ProfiledGeneric> profiledBinaries = new Vector<ProfiledGeneric>();

    public AddrBinaryTable(GppTraceGraph myGraph, Composite parent, GppModelAdapter adapter) {
        super(myGraph, parent, adapter);
    }

    public void createTableViewer(int drawMode) {
        if (this.parent == null)
            return;

        // Binary table:
        //      checkbox + colored or white background
        //     percent load
        //     binary name
        //      binary path
        //      sample count

        // Check the drawMode
        switch (drawMode) {
        case Defines.BINARIES:
        case Defines.THREADS_FUNCTIONS_BINARIES:
        case Defines.THREADS_BINARIES:
        case Defines.FUNCTIONS_THREADS_BINARIES:
        case Defines.FUNCTIONS_BINARIES:
        case Defines.THREADS_BINARIES_FUNCTIONS:
        case Defines.BINARIES_THREADS:
        case Defines.BINARIES_THREADS_FUNCTIONS:
        case Defines.BINARIES_FUNCTIONS:
        case Defines.BINARIES_FUNCTIONS_THREADS:
        case Defines.FUNCTIONS_BINARIES_THREADS: {
            break;
        }
        default:
            // no binary table in this draw mode
            return;
        }

        // create the table viewer
        this.tableViewer = CheckboxTableViewer.newCheckList(this.parent,
                SWT.BORDER | SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION);

        // add the check state handler, label provider and content provider
        tableViewer.addCheckStateListener(new SharedCheckHandler());
        tableViewer.setLabelProvider(new shownBinariesLabelProvider());
        tableViewer.setContentProvider(new shownBinariesContentProvider());
        tableViewer.addSelectionChangedListener(new ISelectionChangedListener() {
            public void selectionChanged(SelectionChangedEvent arg0) {
                if (copyAction == null)
                    return;

                // when selection changes, the ability to copy may change
                copyAction.setEnabled(table.getSelectionCount() > 0);
                PIPageEditor.getActionBars().updateActionBars();
            }
        });

        createDefaultActions();

        // make sure the table viewer has a sorter
        if (this.sorter == null)
            this.sorter = new GppTableSorter();

        this.table = tableViewer.getTable();
        this.table.setRedraw(false);

        // give the table a heading for use in copying and exported
        this.table.setData(Messages.getString("AddrBinaryTable.binaries")); //$NON-NLS-1$

        // create the columns
        TableColumn column;

        // data associated with the TableViewer will note which columns contain hex values
        // Keep this in the order in which columns have been created
        boolean[] isHex = { false, false, false, false, false };
        this.table.setData("isHex", isHex); //$NON-NLS-1$

        // select/deselect column
        column = new TableColumn(table, SWT.CENTER);
        column.setText(COLUMN_HEAD_SHOW);
        column.setWidth(COLUMN_WIDTH_SHOW);
        column.setData(Integer.valueOf(COLUMN_ID_SHOW));
        column.setMoveable(true);
        column.setResizable(true);
        column.addSelectionListener(new ColumnSelectionHandler());

        // percent load column
        column = new TableColumn(table, SWT.RIGHT);
        column.setText(COLUMN_HEAD_PERCENT_LOAD);
        column.setWidth(COLUMN_WIDTH_PERCENT_LOAD);
        column.setData(Integer.valueOf(COLUMN_ID_PERCENT_LOAD));
        column.setMoveable(true);
        column.setResizable(true);
        column.addSelectionListener(new ColumnSelectionHandler());

        // binary name column
        column = new TableColumn(table, SWT.LEFT);
        column.setText(COLUMN_HEAD_BINARY);
        column.setWidth(COLUMN_WIDTH_BINARY_NAME);
        column.setData(Integer.valueOf(COLUMN_ID_BINARY));
        column.setMoveable(true);
        column.setResizable(true);
        column.addSelectionListener(new ColumnSelectionHandler());

        // path column
        column = new TableColumn(table, SWT.LEFT);
        column.setText(COLUMN_HEAD_PATH);
        column.setWidth(COLUMN_WIDTH_PATH);
        column.setData(Integer.valueOf(COLUMN_ID_PATH));
        column.setMoveable(true);
        column.setResizable(true);
        column.addSelectionListener(new ColumnSelectionHandler());

        // sample count column
        column = new TableColumn(table, SWT.CENTER);
        column.setText(COLUMN_HEAD_SAMPLE_COUNT);
        column.setWidth(COLUMN_WIDTH_SAMPLE_COUNT);
        column.setData(Integer.valueOf(COLUMN_ID_SAMPLE_COUNT));
        column.setMoveable(true);
        column.setResizable(true);
        column.addSelectionListener(new ColumnSelectionHandler());

        // listen for mouse clicks: to select a row, pop up a menu, etc.
        table.addMouseListener(new TableMouseListener());

        // listen for key sequences such as Ctrl-A and Ctrl-C
        table.addKeyListener(new TableKeyListener());

        table.addFocusListener(new AddrTableFocusListener());

        // add form data in case later we add a sash to the right
        FormData viewerData = new FormData();
        viewerData.top = new FormAttachment(0);
        viewerData.bottom = new FormAttachment(100);
        viewerData.left = new FormAttachment(0);
        viewerData.right = new FormAttachment(100);
        table.setLayoutData(viewerData);
        table.setLayout(new FormLayout());

        // make sure the table viewer has a sorter
        if (this.sorter == null)
            this.sorter = new GppTableSorter();

        table.setHeaderVisible(true);
        table.setLinesVisible(true);
        table.setRedraw(true);
    }

    public void setTableViewer(CheckboxTableViewer tableViewer) {
        this.tableViewer = tableViewer;

        if (tableViewer == null)
            this.table = null;
    }

    public void setTableViewer(int drawMode) {
        if (this.parent == null)
            return;

        createTableViewer(drawMode);

        // sort by sample count
        this.sortColumn = COLUMN_ID_SAMPLE_COUNT;
        this.sortAscending = false;

        // profiledBinaries and tableItemData contain one entry per table row
        updateProfiledAndItemData(false);
        quickSort(sortColumn, profiledBinaries);

        // initially, all rows are selected
        this.tableViewer.setAllChecked(true);
    }

    public void refreshTableViewer() {
        if (this.tableViewer == null)
            return;

        this.tableViewer.setInput(tableItemData);

        addColor(this.myGraph.getDrawMode());
    }

    public void addColor(int drawMode) {
        if (this.tableViewer == null)
            return;

        // make sure that this table's colors are being shown
        if ((drawMode != Defines.BINARIES) && (drawMode != Defines.THREADS_BINARIES)
                && (drawMode != Defines.THREADS_FUNCTIONS_BINARIES) && (drawMode != Defines.FUNCTIONS_BINARIES)
                && (drawMode != Defines.FUNCTIONS_THREADS_BINARIES))
            return;

        ProfiledGeneric pGeneric;

        TableItem[] items = this.table.getItems();

        for (int i = 0; i < items.length; i++) {
            pGeneric = (ProfiledGeneric) items[i].getData();
            //         Color color = ((GppTrace)this.myGraph.getTrace()).getBinaryColorPalette().getColor(pGeneric.getNameString());
            items[i].setBackground(COLOR_COLUMN_INDEX, pGeneric.getColor()); // checkbox and color are always in the first column
        }

        table.redraw();
    }

    public void removeColor(int drawMode) {
        if (this.tableViewer == null)
            return;

        // make sure that this table's colors should not be shown
        if ((drawMode == Defines.BINARIES) || (drawMode == Defines.THREADS_BINARIES)
                || (drawMode == Defines.THREADS_FUNCTIONS_BINARIES) || (drawMode == Defines.FUNCTIONS_BINARIES)
                || (drawMode == Defines.FUNCTIONS_THREADS_BINARIES))
            return;

        TableItem[] items = this.table.getItems();

        for (int i = 0; i < items.length; i++) {
            // checkbox and color are always in the first column
            items[i].setBackground(COLOR_COLUMN_INDEX, this.parent.getDisplay().getSystemColor(SWT.COLOR_WHITE));
        }

        table.redraw();
    }

    private static class shownBinariesContentProvider implements IStructuredContentProvider {

        public shownBinariesContentProvider() {
            super();
        }

        public Object[] getElements(Object inputElement) {
            return ((Vector) inputElement).toArray();
        }

        public void dispose() {
        }

        public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
        }
    }

    private class shownBinariesLabelProvider extends LabelProvider implements ITableLabelProvider {

        public shownBinariesLabelProvider() {
            super();
        }

        /* (non-Javadoc)
         * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnText(java.lang.Object, int)
         */
        public String getColumnText(Object element, int columnIndex) {

            int columnId = ((Integer) table.getColumn(columnIndex).getData()).intValue();

            if (element instanceof ProfiledThreshold) {
                ProfiledThreshold pThreshold = (ProfiledThreshold) element;
                switch (columnId) {
                case COLUMN_ID_SHOW: {
                    return SHOW_ITEM_VALUE;
                }
                case COLUMN_ID_PERCENT_LOAD: {
                    // Percent load string
                    double startTime = PIPageEditor.currentPageEditor().getStartTime();
                    double endTime = PIPageEditor.currentPageEditor().getEndTime();
                    if ((startTime == -1) || (endTime == -1) || (startTime == endTime)) {
                        pThreshold.setAverageLoadValueString(myGraph.getGraphIndex(), ""); //$NON-NLS-1$
                    } else {
                        float load = (float) (pThreshold.getSampleCount(myGraph.getGraphIndex())
                                / (endTime - startTime) / 10.0);

                        if (load < 0.005)
                            pThreshold.setAverageLoadValueString(myGraph.getGraphIndex(),
                                    Messages.getString("AddrBinaryTable.zeroFormat")); //$NON-NLS-1$
                        else
                            pThreshold.setAverageLoadValueString(myGraph.getGraphIndex(), load);
                    }
                    return pThreshold.getAverageLoadValueString(myGraph.getGraphIndex());
                }
                case COLUMN_ID_BINARY: {
                    DecimalFormat timeFormat = new DecimalFormat(
                            Messages.getString("AddrBinaryTable.decimalFormat")); //$NON-NLS-1$
                    int count = pThreshold.getItemCount();

                    return count
                            + (count > 1 ? Messages.getString("AddrBinaryTable.threshold1") //$NON-NLS-1$
                                    : Messages.getString("AddrBinaryTable.threshold2")) //$NON-NLS-1$
                            + timeFormat
                                    .format((Double) NpiInstanceRepository.getInstance().activeUidGetPersistState(
                                            "com.nokia.carbide.cpp.pi.address.thresholdLoadBinary") * 100.0) //$NON-NLS-1$
                            + Messages.getString("AddrBinaryTable.threshold3") //$NON-NLS-1$ 
                            + (Integer) NpiInstanceRepository.getInstance().activeUidGetPersistState(
                                    "com.nokia.carbide.cpp.pi.address.thresholdCountBinary") //$NON-NLS-1$
                            + Messages.getString("AddrBinaryTable.threshold4"); //$NON-NLS-1$   
                }
                case COLUMN_ID_SAMPLE_COUNT: {
                    // Sample count
                    return String.valueOf(pThreshold.getSampleCount(myGraph.getGraphIndex()));
                }
                default: {
                    return ""; //$NON-NLS-1$
                }
                }
            }

            if (!(element instanceof ProfiledBinary))
                return ""; //$NON-NLS-1$

            ProfiledBinary profiledItem = (ProfiledBinary) element;

            switch (columnId) {
            case COLUMN_ID_SHOW: {
                return SHOW_ITEM_VALUE;
            }
            case COLUMN_ID_PERCENT_LOAD: {
                // Percent load string
                return profiledItem.getAverageLoadValueString(myGraph.getGraphIndex());
            }
            case COLUMN_ID_BINARY: {
                // Binary
                String binary = profiledItem.getNameString();
                int index = binary.lastIndexOf('\\');
                if (index == -1)
                    return binary;
                else
                    return binary.substring(index + 1);
            }
            case COLUMN_ID_PATH: {
                // Path
                String binary = profiledItem.getNameString();
                int index = binary.lastIndexOf('\\');
                if (index == -1)
                    return ""; //$NON-NLS-1$
                else
                    return binary.substring(0, index);
            }
            case COLUMN_ID_SAMPLE_COUNT: {
                // Sample count
                return String.valueOf(profiledItem.getSampleCount(myGraph.getGraphIndex()));
            }
            default: {
                break;
            }
            }
            // should never get here
            return ""; //$NON-NLS-1$
        }

        public Image getColumnImage(Object element, int columnIndex) {
            return null;
        }
    }

    @Override
    public void action(String actionString) {
        int graphIndex = this.myGraph.getGraphIndex();

        if (actionString.equals("add") //$NON-NLS-1$
                || actionString.equals("remove")) //$NON-NLS-1$
        {
            actionAddRemove(actionString, graphIndex);
            return;
        } else if (actionString.equals("addall") //$NON-NLS-1$
                || actionString.equals("removeall")) //$NON-NLS-1$
        {
            actionAddRemoveAll(actionString, graphIndex);
            return;
        } else if (actionString.equals("recolor")) //$NON-NLS-1$
        {
            actionRecolor();
            return;
        } else if (actionString.equals("copy")) //$NON-NLS-1$
        {
            actionCopyOrSave(true, this.table, CHECKBOX_NO_TEXT, false, "\t", "\n"); //$NON-NLS-1$ //$NON-NLS-2$
            return;
        } else if (actionString.equals("copyTable")) //$NON-NLS-1$
        {
            actionCopyOrSave(true, this.table, CHECKBOX_NO_TEXT, true, "\t", "\n"); //$NON-NLS-1$ //$NON-NLS-2$
            return;
        } else if (actionString.equals("copyDrilldown")) //$NON-NLS-1$
        {
            actionCopyOrSaveDrilldown(true, "\t"); //$NON-NLS-1$
            return;
        } else if (actionString.equals("saveTable")) //$NON-NLS-1$
        {
            actionCopyOrSave(false, this.table, CHECKBOX_NO_TEXT, true, ",", "\n"); //$NON-NLS-1$ //$NON-NLS-2$
            return;
        } else if (actionString.equals("saveDrilldown")) //$NON-NLS-1$
        {
            actionCopyOrSaveDrilldown(false, ","); //$NON-NLS-1$
            return;
        } else if (actionString.equals("saveSamples")) //$NON-NLS-1$
        {
            SaveSampleString saveSampleString = new SaveSampleString(graphIndex, myGraph.getDrawMode());
            actionSaveSamples(saveSampleString); //$NON-NLS-1$
            return;
        } else if (actionString.equals("selectAll")) //$NON-NLS-1$
        {
            actionSelectAll();
            return;
        } else if (actionString.equals("doubleClick")) //$NON-NLS-1$
        {
            copyAction.setEnabled(false);
            PIPageEditor.getActionBars().updateActionBars();
            return;
        } else if (actionString.equals("sortfullpath")) //$NON-NLS-1$
        {
            if (this.sortColumn == COLUMN_ID_FULL_PATH)
                // sort in other order
                sortAscending = !sortAscending;
            else
                // sort in ascending order
                sortAscending = true;
            sortColumn = COLUMN_ID_FULL_PATH;
            quickSort(sortColumn, profiledBinaries);
            return;
        } else if (actionString.equals("binary-only")) //$NON-NLS-1$
        {
            actionBinary();
            return;
        } else if (actionString.equals("binary-thread")) //$NON-NLS-1$
        {
            actionBinaryThread();
            return;
        } else if (actionString.equals("binary-thread-function")) //$NON-NLS-1$
        {
            actionBinaryThreadFunction();
            return;
        } else if (actionString.equals("binary-function")) //$NON-NLS-1$
        {
            actionBinaryFunction();
            return;
        } else if (actionString.equals("binary-function-thread")) //$NON-NLS-1$
        {
            actionBinaryFunctionThread();
            return;
        } else if ((actionString.equals("thread-only")) //$NON-NLS-1$
                || (actionString.equals("thread-binary")) //$NON-NLS-1$
                || (actionString.equals("thread-binary-function")) //$NON-NLS-1$
                || (actionString.equals("thread-function")) //$NON-NLS-1$
                || (actionString.equals("thread-function-binary"))) //$NON-NLS-1$
        {
            // let the thread page action handler handle it
            this.myGraph.getThreadTable().action(actionString);
            return;
        } else if ((actionString.equals("function-only")) //$NON-NLS-1$
                || (actionString.equals("function-thread")) //$NON-NLS-1$
                || (actionString.equals("function-thread-binary")) //$NON-NLS-1$
                || (actionString.equals("function-binary")) //$NON-NLS-1$
                || (actionString.equals("function-binary-thread"))) //$NON-NLS-1$
        {
            // let the function page action handler handle it
            this.myGraph.getFunctionTable().action(actionString);
            return;
        } else if (actionString.equals("changeThresholdBinary")) //$NON-NLS-1$
        {
            ProfiledThreshold threshold = this.myGraph.getThresholdBinary();
            boolean enabled = threshold.isEnabled(graphIndex);

            this.tableItemData.clear();
            this.profiledBinaries.clear();
            this.myGraph.getSortedBinaries().clear();
            if (threshold.getItems() != null)
                threshold.getItems().clear();
            adapter.init(threshold, graphIndex);

            // if this appears, it needs to be the first item, so that it is drawn at the bottom
            myGraph.getSortedBinaries().add(threshold);

            int binaryThreshold = (Integer) NpiInstanceRepository.getInstance()
                    .activeUidGetPersistState("com.nokia.carbide.cpp.pi.address.thresholdCountBinary"); //$NON-NLS-1$
            for (int i = 0; i < this.myGraph.getGppTrace().getSortedBinaries().size(); i++) {
                ProfiledGeneric nextElement = (ProfiledGeneric) this.myGraph.getGppTrace().getSortedBinaries()
                        .get(i);
                if (adapter.getTotalSampleCount(nextElement) < binaryThreshold) {
                    nextElement.setEnabled(graphIndex, enabled);
                    adapter.addItem(threshold, graphIndex, nextElement, 0);
                } else {
                    tableItemData.add(nextElement);
                    profiledBinaries.add(nextElement);
                    myGraph.getSortedBinaries().add(nextElement);
                }
            }

            if (threshold.getItemCount() != 0) {
                tableItemData.add(threshold);
                profiledBinaries.add(threshold);
            } else {
                // remove the threshold item
                myGraph.getSortedBinaries().remove(0);
            }

            refreshTableViewer();
            threshold.setEnabled(graphIndex, enabled);

            // make sure that checkboxes shown reflect the actual enabled values
            TableItem[] tableItems = this.table.getItems();
            for (int i = 0; i < tableItems.length; i++) {
                if (tableItems[i].getData() instanceof ProfiledGeneric) {
                    ProfiledGeneric pGeneric = (ProfiledGeneric) tableItems[i].getData();
                    if (tableItems[i].getChecked() != pGeneric.isEnabled(graphIndex))
                        tableItems[i].setChecked(pGeneric.isEnabled(graphIndex));
                }
            }

            this.myGraph.genericRefreshCumulativeThreadTable();
        } else if (actionString.equals("saveTableTest")) //$NON-NLS-1$
        {
            // copy save file contents to the clipboard for easy viewing
            Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
            SaveTableString getString = new SaveTableString(this.table, CHECKBOX_NO_TEXT,
                    Messages.getString("AddrBinaryTable.comma"), "\n"); //$NON-NLS-1$ //$NON-NLS-2$
            String copyString = getString.getData();
            StringSelection contents = new StringSelection(copyString);
            cb.setContents(contents, contents);
            return;
        } else if (actionString.equals("saveDrilldownTest")) //$NON-NLS-1$
        {
            // copy save file contents to the clipboard for easy viewing
            Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
            Table[] tables = getDrillDownTables();

            int tableCount = 0;
            while (tableCount < tables.length && tables[tableCount] != null)
                tableCount++;

            SaveDrillDownString getString = new SaveDrillDownString(tableCount, tables,
                    Messages.getString("AddrBinaryTable.comma")); //$NON-NLS-1$
            String copyString = getString.getData();
            StringSelection contents = new StringSelection(copyString);
            cb.setContents(contents, contents);
            return;
        }
    }

    private void actionAddRemove(String actionString, int graphIndex) {
        ProfiledGeneric pGeneric;
        int totalSamples = 0;

        // true for "add", false for "remove"
        boolean addIt = actionString.equals("add"); //$NON-NLS-1$

        TableItem[] selectedItems = this.table.getSelection();
        for (int i = 0; i < selectedItems.length; i++) {
            selectedItems[i].setChecked(addIt);
            Object item = ((TableItem) selectedItems[i]).getData();
            if (item instanceof ProfiledGeneric) {
                pGeneric = (ProfiledGeneric) item;
                pGeneric.setEnabled(graphIndex, addIt);
                totalSamples += pGeneric.getSampleCount(graphIndex);

                if (item instanceof ProfiledThreshold) {
                    ProfiledThreshold pThreshold = (ProfiledThreshold) item;
                    for (ProfiledGeneric p : pThreshold.getItems())
                        p.setEnabled(graphIndex, addIt);
                }
            }
        }

        // this table's set of checkbox-selected rows has changed,
        // so propagate that information
        Object[] selectedValues = this.tableViewer.getCheckedElements();
        String[] nameList = new String[selectedValues.length];

        for (int i = 0; i < selectedValues.length; i++) {
            if (selectedValues[i] instanceof ProfiledGeneric) {
                pGeneric = (ProfiledGeneric) selectedValues[i];
                nameList[i] = pGeneric.getNameString();
            }
        }

        PIVisualSharedData shared = myGraph.getSharedDataInstance();
        shared.gppSelectedBinaryNames = nameList;

        if ((totalSamples != 0) || (myGraph.getDrawMode() == Defines.BINARIES))
            selectionChangeNotify();

        this.table.deselectAll();
    }

    private void actionAddRemoveAll(String actionString, int graphIndex) {
        ProfiledGeneric pGeneric;

        // true for "add", false for "remove"
        boolean addIt = actionString.equals("addall"); //$NON-NLS-1$

        TableItem[] selectedItems = this.table.getItems();
        String[] nameList = new String[selectedItems.length];
        for (int i = 0; i < selectedItems.length; i++) {
            selectedItems[i].setChecked(addIt);
            Object item = ((TableItem) selectedItems[i]).getData();
            if (item instanceof ProfiledGeneric) {
                pGeneric = (ProfiledGeneric) item;
                pGeneric.setEnabled(graphIndex, addIt);
                nameList[i] = pGeneric.getNameString();

                if (item instanceof ProfiledThreshold) {
                    ProfiledThreshold pThreshold = (ProfiledThreshold) item;
                    for (ProfiledGeneric p : pThreshold.getItems())
                        p.setEnabled(graphIndex, addIt);
                }
            }
        }

        // this table's set of checkbox-selected rows has changed,
        // so propagate that information
        PIVisualSharedData shared = myGraph.getSharedDataInstance();
        shared.gppSelectedBinaryNames = nameList;

        selectionChangeNotify();
        this.table.deselectAll();
    }

    private void actionRecolor() {
        int uid = this.myGraph.getUid();
        GppTrace gppTrace = this.myGraph.getGppTrace();

        // recolor selected items
        boolean didRecolor = false;

        TableItem[] selectedItems = table.getSelection();
        TableColorPalette palette = ((GppTrace) this.myGraph.getTrace()).getBinaryColorPalette();
        for (int i = 0; i < selectedItems.length; i++) {
            if (selectedItems[i].getData() instanceof ProfiledGeneric) {
                ProfiledGeneric pGeneric = (ProfiledGeneric) selectedItems[i].getData();
                String nameKey = pGeneric.getNameString();
                if (palette.recolorEntryDialog(table.getShell(), nameKey)) {
                    Color color = palette.getColor(nameKey);
                    Color oldColor = pGeneric.getColor();

                    if (color.equals(oldColor))
                        continue;

                    didRecolor = true;

                    if (!(pGeneric instanceof ProfiledThreshold)) {
                        PIPageEditor.currentPageEditor().setDirty();
                        pGeneric.setColor(color);
                    } else {
                        // for the threshold item, we must change every graph's thread threshold item
                        // CH: refactor! This could be done via an observer pattern. This class should not have knowledge of all other graphs  
                        gppTrace.getGppGraph(PIPageEditor.THREADS_PAGE, uid).getThresholdThread().setColor(color);
                        gppTrace.getGppGraph(PIPageEditor.BINARIES_PAGE, uid).getThresholdThread().setColor(color);
                        gppTrace.getGppGraph(PIPageEditor.FUNCTIONS_PAGE, uid).getThresholdThread().setColor(color);
                        //                  if (gppTrace.getCPUCount() > 1){ //SMP CH: comment this out once we have SMP graphs on the binaries page
                        //                     for (int cpu = 0; cpu < gppTrace.getCPUCount(); cpu++) {
                        //                        gppTrace.getGppGraph(7 + cpu, uid).getThresholdThread().setColor(color);                                             
                        //                     }
                        //                  }
                    }
                }

                // recoloring should only be done in a draw mode that displays this table's colors
                selectedItems[i].setBackground(COLOR_COLUMN_INDEX, palette.getColor(nameKey));
            }
        }

        if (!didRecolor)
            return;

        table.redraw();
        this.myGraph.repaint();
        this.myGraph.setGraphImageChanged(true); // any selection change to drill down will change graph

        // if any other tabs are displaying this type of graph, they need to be scheduled for redrawing
        for (int i = 0; i < 3; i++) {
            IGppTraceGraph graph = gppTrace.getGppGraph(i, uid);

            if (graph == this.myGraph)
                continue;

            int drawMode = graph.getDrawMode();

            if ((drawMode == Defines.BINARIES) || (drawMode == Defines.THREADS_BINARIES)
                    || (drawMode == Defines.THREADS_FUNCTIONS_BINARIES) || (drawMode == Defines.FUNCTIONS_BINARIES)
                    || (drawMode == Defines.FUNCTIONS_THREADS_BINARIES)) {
                graph.getBinaryTable().addColor(drawMode);
                graph.setGraphImageChanged(true); // any selection change to drill down will change graph
                graph.repaint();
            }
        }
    }

    private void actionBinary() {
        // current drawMode should be BINARIES, BINARIES_THREADS, BINARIES_THREADS_FUNCTIONS,
        // BINARIES_FUNCTIONS, or BINARIES_FUNCTIONS_THREADS
        int drawMode = this.myGraph.getDrawMode();

        if ((drawMode != Defines.BINARIES_THREADS) && (drawMode != Defines.BINARIES_THREADS_FUNCTIONS)
                && (drawMode != Defines.BINARIES_FUNCTIONS) && (drawMode != Defines.BINARIES_FUNCTIONS_THREADS)) {
            // this case should be drawMode == Defines.BINARIES
            return;
        }

        setIsDrilldown(false);

        // get rid of any existing tables and sashes
        if (this.myGraph.getLeftSash() != null) {
            this.myGraph.getLeftSash().dispose();
            this.myGraph.setLeftSash(null);

            // detach the table from the sash
            try {
                FormData formData = (FormData) this.table.getLayoutData();
                formData.right = new FormAttachment(100);
            } catch (ClassCastException e1) {
            }
        }
        if (this.myGraph.getRightSash() != null) {
            this.myGraph.getRightSash().dispose();
            this.myGraph.setRightSash(null);
        }
        if ((this.myGraph.getThreadTable() != null) && (this.myGraph.getThreadTable().getTable() != null)) {
            this.myGraph.getThreadTable().getTableViewer().getTable().dispose();
            this.myGraph.getThreadTable().setTableViewer(null);
        }
        if ((this.myGraph.getFunctionTable() != null) && (this.myGraph.getFunctionTable().getTable() != null)) {
            this.myGraph.getFunctionTable().getTableViewer().getTable().dispose();
            this.myGraph.getFunctionTable().setTableViewer(null);
        }

        // set the draw mode
        this.myGraph.setDrawMode(Defines.BINARIES);

        // show colors in the rightmost table
        addColor(Defines.BINARIES);

        this.parent.layout();

        this.myGraph.repaint();
    }

    private void actionBinaryThread() {
        // current drawMode should be BINARIES, BINARIES_THREADS, or BINARIES_THREADS_FUNCTIONS
        int drawMode = this.myGraph.getDrawMode();
        int graphIndex = this.myGraph.getGraphIndex();

        if (drawMode != Defines.BINARIES && drawMode != Defines.BINARIES_THREADS_FUNCTIONS) {
            return;
        }

        setIsDrilldown(true);

        if (drawMode == Defines.BINARIES) {

            // set the draw mode before populating table viewers, since the
            // color column depends on the draw mode
            this.myGraph.setDrawMode(Defines.BINARIES_THREADS);

            // create the thread graph table viewer
            AddrThreadTable threadTable = this.myGraph.getThreadTable();
            threadTable.createTableViewer(Defines.BINARIES_THREADS);
            threadTable.setIsDrilldown(true);

            // create a reduced set of thread entries based on enabled binary entries
            GppTrace gppTrace = (GppTrace) this.myGraph.getTrace();
            gppTrace.setBinaryThread(graphIndex, adapter, this.myGraph.getProfiledThreads());

            // put check marks on all rows, and sort by sample count
            threadTable.quickSort(COLUMN_ID_SAMPLE_COUNT, this.myGraph.getProfiledThreads());
            threadTable.updateProfiledAndItemData(true);
            threadTable.getTableViewer().setAllChecked(true);
            threadTable.getTableViewer().refresh();

            // put check marks on all enabled binary rows
            for (int i = 0; i < this.table.getItemCount(); i++) {
                ProfiledGeneric pBinary = (ProfiledGeneric) this.table.getItem(i).getData();
                if (pBinary.isEnabled(graphIndex))
                    this.table.getItem(i).setChecked(true);
            }

            // remove colors where appropriate
            removeColor(Defines.BINARIES_THREADS);

            // connect the tables with a sash
            Sash leftSash = new Sash(this.parent, SWT.VERTICAL);

            final FormData leftSashData = new FormData();
            leftSashData.top = new FormAttachment(0);
            leftSashData.bottom = new FormAttachment(100);
            leftSashData.left = new FormAttachment(50); // middle
            leftSash.setLayoutData(leftSashData);

            final Composite sashParent = this.parent;
            leftSash.addListener(SWT.Selection, new Listener() {
                public void handleEvent(Event event) {
                    if (event.detail != SWT.DRAG) {
                        leftSashData.left = new FormAttachment(0, event.x);
                        sashParent.layout();
                    }
                }
            });

            myGraph.setLeftSash(leftSash);

            // attach the thread table to the sash
            final FormData viewerData = new FormData();
            viewerData.top = new FormAttachment(0);
            viewerData.bottom = new FormAttachment(100);
            viewerData.left = new FormAttachment(leftSash);
            viewerData.right = new FormAttachment(100);
            threadTable.getTable().setLayoutData(viewerData);

            // attach the binary table to the sash
            try {
                FormData formData = (FormData) this.table.getLayoutData();
                formData.right = new FormAttachment(leftSash);
            } catch (ClassCastException e1) {
            }

            this.parent.layout();

            this.myGraph.repaint();

        } else if (drawMode == Defines.BINARIES_THREADS_FUNCTIONS) {

            // get rid of the function table and its sash
            if (this.myGraph.getRightSash() != null) {
                this.myGraph.getRightSash().dispose();
                this.myGraph.setRightSash(null);
            }
            if ((this.myGraph.getFunctionTable() != null) && (this.myGraph.getFunctionTable().getTable() != null)) {
                this.myGraph.getFunctionTable().getTableViewer().getTable().dispose();
                this.myGraph.getFunctionTable().setTableViewer(null);
            }

            // get rid of the middle table's connection to the sash
            try {
                FormData formData = (FormData) this.myGraph.getThreadTable().getTable().getLayoutData();
                formData.right = new FormAttachment(100);
            } catch (ClassCastException e1) {
            }

            // move the left sash to the middle
            try {
                FormData formData = (FormData) this.myGraph.getLeftSash().getLayoutData();
                formData.left = new FormAttachment(50); // middle
            } catch (ClassCastException e1) {
            }

            // set the draw mode
            this.myGraph.setDrawMode(Defines.BINARIES_THREADS);

            // show colors in the rightmost table
            this.myGraph.getThreadTable().addColor(Defines.BINARIES_THREADS);

            this.parent.layout();

            this.myGraph.repaint();
        }

        // this case should be drawMode == Defines.BINARIES_THREADS
        return;
    }

    private void actionBinaryThreadFunction() {
        // current drawMode should be BINARIES_THREADS or BINARIES_THREADS_FUNCTIONS
        int drawMode = this.myGraph.getDrawMode();
        int graphIndex = this.myGraph.getGraphIndex();

        if (drawMode != Defines.BINARIES_THREADS) {
            // this case should be drawMode == Defines.BINARIES_THREADS_FUNCTIONS
            return;
        }

        setIsDrilldown(true);

        // set the draw mode before populating table viewers, since the
        // color column depends on the draw mode
        this.myGraph.setDrawMode(Defines.BINARIES_THREADS_FUNCTIONS);

        // create the function graph table viewer
        AddrFunctionTable functionTable = this.myGraph.getFunctionTable();
        functionTable.createTableViewer(Defines.BINARIES_THREADS_FUNCTIONS);
        functionTable.setIsDrilldown(true);

        // create a reduced set of function entries based on enabled thread and binary entries
        GppTrace gppTrace = (GppTrace) this.myGraph.getTrace();
        gppTrace.setBinaryThreadFunction(graphIndex, adapter, this.myGraph.getProfiledFunctions());

        // put check marks on all rows, and sort by sample count
        functionTable.quickSort(COLUMN_ID_SAMPLE_COUNT, this.myGraph.getProfiledFunctions());
        functionTable.updateProfiledAndItemData(true);
        functionTable.getTableViewer().setAllChecked(true);
        functionTable.getTableViewer().refresh();

        // remove colors where appropriate
        this.myGraph.getThreadTable().removeColor(Defines.BINARIES_THREADS_FUNCTIONS);

        // connect the 2nd and 3rd tables with a sash
        Sash rightSash = new Sash(this.parent, SWT.VERTICAL);

        final FormData rightSashData = new FormData();
        rightSashData.top = new FormAttachment(0);
        rightSashData.bottom = new FormAttachment(100);
        rightSashData.left = new FormAttachment(67); // two thirds
        rightSash.setLayoutData(rightSashData);

        final Composite sashParent = this.parent;
        rightSash.addListener(SWT.Selection, new Listener() {
            public void handleEvent(Event event) {
                if (event.detail != SWT.DRAG) {
                    rightSashData.left = new FormAttachment(0, event.x);
                    sashParent.layout();
                }
            }
        });

        myGraph.setRightSash(rightSash);

        // attach the function table to the sash
        final FormData viewerData = new FormData();
        viewerData.top = new FormAttachment(0);
        viewerData.bottom = new FormAttachment(100);
        viewerData.left = new FormAttachment(rightSash);
        viewerData.right = new FormAttachment(100);
        functionTable.getTable().setLayoutData(viewerData);

        // attach the thread table to the sash
        try {
            FormData formData = (FormData) this.myGraph.getThreadTable().getTable().getLayoutData();
            formData.right = new FormAttachment(rightSash);
        } catch (ClassCastException e1) {
        }

        // move the left sash to 1/3 from the left
        try {
            FormData formData = (FormData) this.myGraph.getLeftSash().getLayoutData();
            formData.left = new FormAttachment(33); // one third
        } catch (ClassCastException e1) {
        }

        this.parent.layout();

        this.myGraph.repaint();
    }

    private void actionBinaryFunction() {
        // current drawMode should be BINARIES, BINARIES_FUNCTIONS, or BINARIES_FUNCTIONS_THREADS
        int drawMode = this.myGraph.getDrawMode();
        int graphIndex = this.myGraph.getGraphIndex();

        if (drawMode != Defines.BINARIES && drawMode != Defines.BINARIES_FUNCTIONS_THREADS) {
            return;
        }

        setIsDrilldown(true);

        if (drawMode == Defines.BINARIES) {

            // set the draw mode
            this.myGraph.setDrawMode(Defines.BINARIES_FUNCTIONS);

            // create the function graph table viewer
            AddrFunctionTable functionTable = this.myGraph.getFunctionTable();
            functionTable.createTableViewer(Defines.BINARIES_FUNCTIONS);
            functionTable.setIsDrilldown(true);

            // create a reduced set of function entries based on enabled binary entries
            GppTrace gppTrace = (GppTrace) this.myGraph.getTrace();
            Vector<ProfiledGeneric> functions = this.myGraph.getProfiledFunctions();
            gppTrace.setBinaryFunction(graphIndex, adapter, functions);

            // put check marks on all rows, and sort by sample count
            functionTable.quickSort(COLUMN_ID_SAMPLE_COUNT, this.myGraph.getProfiledFunctions());
            functionTable.updateProfiledAndItemData(true);
            functionTable.getTableViewer().setAllChecked(true);
            functionTable.getTableViewer().refresh();

            // remove colors where appropriate
            removeColor(Defines.BINARIES_FUNCTIONS);

            // connect the tables with a sash
            Sash leftSash = new Sash(this.parent, SWT.VERTICAL);

            final FormData leftSashData = new FormData();
            leftSashData.top = new FormAttachment(0);
            leftSashData.bottom = new FormAttachment(100);
            leftSashData.left = new FormAttachment(50); // middle
            leftSash.setLayoutData(leftSashData);

            final Composite sashParent = this.parent;
            leftSash.addListener(SWT.Selection, new Listener() {
                public void handleEvent(Event event) {
                    if (event.detail != SWT.DRAG) {
                        leftSashData.left = new FormAttachment(0, event.x);
                        sashParent.layout();
                    }
                }
            });

            myGraph.setLeftSash(leftSash);

            // attach the function table to the sash
            final FormData viewerData = new FormData();
            viewerData.top = new FormAttachment(0);
            viewerData.bottom = new FormAttachment(100);
            viewerData.left = new FormAttachment(leftSash);
            viewerData.right = new FormAttachment(100);
            functionTable.getTable().setLayoutData(viewerData);

            // attach the binary table to the sash
            try {
                FormData formData = (FormData) this.table.getLayoutData();
                formData.right = new FormAttachment(leftSash);
            } catch (ClassCastException e1) {
            }

            this.parent.layout();

            this.myGraph.repaint();

        } else if (drawMode == Defines.BINARIES_FUNCTIONS_THREADS) {

            // get rid of the threads table and its sash
            if (this.myGraph.getRightSash() != null) {
                this.myGraph.getRightSash().dispose();
                this.myGraph.setRightSash(null);
            }

            if ((this.myGraph.getThreadTable() != null) && (this.myGraph.getThreadTable().getTable() != null)) {
                this.myGraph.getThreadTable().getTableViewer().getTable().dispose();
                this.myGraph.getThreadTable().setTableViewer(null);
            }

            // get rid of the middle table's connection to the sash
            try {
                FormData formData = (FormData) this.myGraph.getFunctionTable().getTable().getLayoutData();
                formData.right = new FormAttachment(100);
            } catch (ClassCastException e1) {
            }

            // move the left sash to the middle
            try {
                FormData formData = (FormData) this.myGraph.getLeftSash().getLayoutData();
                formData.left = new FormAttachment(50); // middle
            } catch (ClassCastException e1) {
            }

            // set the draw mode
            this.myGraph.setDrawMode(Defines.BINARIES_FUNCTIONS);

            // show colors in the rightmost table
            this.myGraph.getFunctionTable().addColor(Defines.BINARIES_FUNCTIONS);

            this.parent.layout();

            this.myGraph.repaint();
        }

        // this case should be drawMode == Defines.BINARIES_THREADS
        return;
    }

    private void actionBinaryFunctionThread() {
        // current drawMode is BINARIES_FUNCTIONS, or BINARIES_FUNCTIONS_THREADS
        int drawMode = this.myGraph.getDrawMode();
        int graphIndex = this.myGraph.getGraphIndex();

        if (drawMode != Defines.BINARIES_FUNCTIONS) {
            // this case should be drawMode == Defines.BINARIES_FUNCTIONS_THREADS
            return;
        }

        setIsDrilldown(true);

        // set the draw mode
        this.myGraph.setDrawMode(Defines.BINARIES_FUNCTIONS_THREADS);

        // create the thread graph table viewer
        AddrThreadTable threadTable = this.myGraph.getThreadTable();
        threadTable.createTableViewer(Defines.BINARIES_FUNCTIONS_THREADS);
        threadTable.setIsDrilldown(true);

        // create a reduced set of thread entries based on enabled binary and function entries
        GppTrace gppTrace = (GppTrace) this.myGraph.getTrace();
        gppTrace.setBinaryFunctionThread(graphIndex, adapter, this.myGraph.getProfiledThreads());

        // put check marks on all rows, and sort by sample count
        threadTable.quickSort(COLUMN_ID_SAMPLE_COUNT, this.myGraph.getProfiledThreads());
        threadTable.updateProfiledAndItemData(true);
        threadTable.getTableViewer().setAllChecked(true);
        threadTable.getTableViewer().refresh();

        // remove colors where appropriate
        this.myGraph.getFunctionTable().removeColor(Defines.BINARIES_FUNCTIONS_THREADS);

        // connect the 2nd and 3rd tables with a sash
        Sash rightSash = new Sash(this.parent, SWT.VERTICAL);

        final FormData rightSashData = new FormData();
        rightSashData.top = new FormAttachment(0);
        rightSashData.bottom = new FormAttachment(100);
        rightSashData.left = new FormAttachment(67); // two thirds
        rightSash.setLayoutData(rightSashData);

        final Composite sashParent = this.parent;
        rightSash.addListener(SWT.Selection, new Listener() {
            public void handleEvent(Event event) {
                if (event.detail != SWT.DRAG) {
                    rightSashData.left = new FormAttachment(0, event.x);
                    sashParent.layout();
                }
            }
        });

        myGraph.setRightSash(rightSash);

        // attach the thread table to the sash
        final FormData viewerData = new FormData();
        viewerData.top = new FormAttachment(0);
        viewerData.bottom = new FormAttachment(100);
        viewerData.left = new FormAttachment(rightSash);
        viewerData.right = new FormAttachment(100);
        threadTable.getTable().setLayoutData(viewerData);

        // attach the function table to the sash
        try {
            FormData formData = (FormData) this.myGraph.getFunctionTable().getTable().getLayoutData();
            formData.right = new FormAttachment(rightSash);
        } catch (ClassCastException e1) {
        }

        // move the left sash to 1/3 from the left
        try {
            FormData formData = (FormData) this.myGraph.getLeftSash().getLayoutData();
            formData.left = new FormAttachment(33); // one third
        } catch (ClassCastException e1) {
        }

        this.parent.layout();

        this.myGraph.repaint();
    }

    @Override
    protected Menu getTableMenu(Decorations aParent, int graphIndex, int drawMode) {
        // get rid of last Menu created so we don't have double menu
        // on click
        if (contextMenu != null) {
            contextMenu.dispose();
        }

        contextMenu = new Menu(aParent, SWT.POP_UP);

        // Use drawMode to determine the drill down items and
        // whether to show a color column
        addDrillDownItems(contextMenu, drawMode);

        // sort by path, then binary
        new MenuItem(contextMenu, SWT.SEPARATOR);
        getSortFullPathItem(contextMenu, this.table.getItemCount() > 0);

        // check and uncheck boxes
        new MenuItem(contextMenu, SWT.SEPARATOR);
        getCheckRows(contextMenu, this.table.getSelectionCount() > 0);

        // select all, copy, and copy all
        new MenuItem(contextMenu, SWT.SEPARATOR);
        getSelectAllItem(contextMenu, this.table.getItemCount() > 0);
        getCopyItem(contextMenu, this.table.getSelectionCount() > 0);
        getCopyTableItem(contextMenu, this.table.getItemCount() > 0);

        // copy drilldown tables, if in drilldown mode
        switch (drawMode) {
        case Defines.THREADS:
        case Defines.BINARIES:
        case Defines.FUNCTIONS:
            getCopyDrilldownItem(contextMenu, false);
            break;
        default:
            getCopyDrilldownItem(contextMenu, true);
            break;
        }

        // save all and save drilldown tables, if in drilldown mode

        new MenuItem(contextMenu, SWT.SEPARATOR);
        getSaveTableItem(contextMenu, this.table.getItemCount() > 0);

        switch (drawMode) {
        case Defines.THREADS:
        case Defines.BINARIES:
        case Defines.FUNCTIONS:
            getSaveDrilldownItem(contextMenu, false);
            break;
        default:
            getSaveDrilldownItem(contextMenu, true);
            break;
        }

        // save raw samples
        boolean haveSamples = false;
        for (int i = 0; !haveSamples && i < profiledBinaries.size(); i++) {
            ProfiledGeneric pg = profiledBinaries.get(i);
            haveSamples = pg.isEnabled(graphIndex) && pg.getSampleCount(graphIndex) > 0;
        }

        double startTime = PIPageEditor.currentPageEditor().getStartTime();
        double endTime = PIPageEditor.currentPageEditor().getEndTime();
        if (!haveSamples || (startTime == -1) || (endTime == -1) || (startTime == endTime))
            getSaveSamplesItem(contextMenu, Messages.getString("AddrBinaryTable.binaries"), false); //$NON-NLS-1$
        else
            getSaveSamplesItem(contextMenu, Messages.getString("AddrBinaryTable.binaries"), true); //$NON-NLS-1$

        // recolor selected threads
        switch (drawMode) {
        case Defines.BINARIES:
        case Defines.THREADS_FUNCTIONS_BINARIES:
        case Defines.THREADS_BINARIES:
        case Defines.FUNCTIONS_THREADS_BINARIES:
        case Defines.FUNCTIONS_BINARIES: {
            // recolor selected items
            new MenuItem(contextMenu, SWT.SEPARATOR);
            getRecolorItem(contextMenu, Messages.getString("AddressPlugin.binaries"), //$NON-NLS-1$
                    this.table.getSelectionCount() > 0);
            break;
        }
        case Defines.THREADS_BINARIES_FUNCTIONS:
        case Defines.BINARIES_THREADS:
        case Defines.BINARIES_THREADS_FUNCTIONS:
        case Defines.BINARIES_FUNCTIONS:
        case Defines.BINARIES_FUNCTIONS_THREADS:
        case Defines.FUNCTIONS_BINARIES_THREADS: {
            break;
        }
        default:
            break;
        }

        new MenuItem(contextMenu, SWT.SEPARATOR);
        getChangeThresholds(contextMenu);

        contextMenu.setVisible(true);

        return contextMenu;
    }

    public void focusGained(FocusEvent e) {
    }

    public void focusLost(FocusEvent e) {
    }

    public void piEventReceived(PIEvent be) {
        if (be.getType() == PIEvent.SELECTION_AREA_CHANGED3) {
            // % loads, % load strings, and/or sample counts have been updated
            // due to a change in the graph area selected, and all table items
            // need to be checked
            updateProfiledAndItemData(true);

            Display.getDefault().syncExec(new Runnable() {
                public void run() {
                    {
                        quickSort(sortColumn, profiledBinaries);
                    }

                    // initially, all rows are selected
                    tableViewer.setAllChecked(true);

                    table.redraw();
                }
            });
        } else if ((be.getType() == PIEvent.SELECTION_AREA_CHANGED2)
                || (be.getType() == PIEvent.CHANGED_BINARY_TABLE)) {
            int graphIndex = this.myGraph.getGraphIndex();

            // This routine does not change which binaries are enabled, but it enables
            // all entries in the 2nd and 3rd tables.
            // It assumes that GppTrace.setSelectedArea(), action("add") or action("remove") has set
            // the % load and sample counts for binaries, except for the threshold list.
            if (be.getType() == PIEvent.SELECTION_AREA_CHANGED2) {
                int thresholdCount = (Integer) NpiInstanceRepository.getInstance()
                        .activeUidGetPersistState("com.nokia.carbide.cpp.pi.address.thresholdCountBinary"); //$NON-NLS-1$
                if (thresholdCount > 0) {
                    Vector<ProfiledGeneric> pGeneric = this.myGraph.getProfiledBinaries();
                    int sampleCount = 0;
                    for (int i = 0; i < pGeneric.size(); i++)
                        if (adapter.getTotalSampleCount(pGeneric.elementAt(i)) < thresholdCount)
                            sampleCount += pGeneric.elementAt(i).getSampleCount(graphIndex);
                    this.myGraph.getThresholdBinary().setSampleCount(graphIndex, sampleCount);
                }
            }

            // redraw this table
            Display.getDefault().syncExec(new Runnable() {
                public void run() {
                    if ((sortColumn == COLUMN_ID_PERCENT_LOAD) || (sortColumn == COLUMN_ID_SAMPLE_COUNT)) {
                        quickSort(sortColumn, profiledBinaries);
                    } else
                        refreshTableViewer();

                    table.redraw();
                }
            });

            int drawMode = this.myGraph.getDrawMode();
            if (drawMode == Defines.BINARIES)
                return;

            GppTrace trace = (GppTrace) (this.myGraph.getTrace());
            PIEvent be3 = new PIEvent(be.getValueObject(), PIEvent.SELECTION_AREA_CHANGED3);

            switch (drawMode) {
            case Defines.BINARIES_THREADS: {
                trace.setBinaryThread(graphIndex, adapter, this.myGraph.getProfiledThreads());

                // update the table items and redraw the table
                this.myGraph.getThreadTable().piEventReceived(be3);
                break;
            }
            case Defines.BINARIES_THREADS_FUNCTIONS: {
                // previous threads are not necessarily graphed this time - reset
                trace.setBinaryThread(graphIndex, adapter, this.myGraph.getProfiledThreads());
                // previous functions are not necessarily graphed this time - reset
                trace.setBinaryThreadFunction(graphIndex, adapter, this.myGraph.getProfiledFunctions());

                // update the table items and redraw the table
                this.myGraph.getThreadTable().piEventReceived(be3);
                this.myGraph.getFunctionTable().piEventReceived(be3);
                break;
            }
            case Defines.BINARIES_FUNCTIONS: {
                trace.setBinaryFunction(graphIndex, adapter, this.myGraph.getProfiledFunctions());

                // update the table items and redraw the table
                this.myGraph.getFunctionTable().piEventReceived(be3);
                break;
            }
            case Defines.BINARIES_FUNCTIONS_THREADS: {
                // previous functions are not necessarily graphed this time - reset
                trace.setBinaryFunction(graphIndex, adapter, this.myGraph.getProfiledFunctions());
                // previous threads are not necessarily graphed this time - reset
                trace.setBinaryFunctionThread(graphIndex, adapter, this.myGraph.getProfiledThreads());

                // update the table items and redraw the table
                this.myGraph.getThreadTable().piEventReceived(be3);
                this.myGraph.getFunctionTable().piEventReceived(be3);
                break;
            }
            default: {
                break;
            }
            }
        } else if (be.getType() == PIEvent.CHANGED_THREAD_TABLE) {
            // This routine enables all entries in the next table

            int drawMode = this.myGraph.getDrawMode();
            if (drawMode != Defines.BINARIES_THREADS_FUNCTIONS)
                return;

            // we don't need to redraw the binary table, since it has not changed

            PIEvent be3 = new PIEvent(be.getValueObject(), PIEvent.SELECTION_AREA_CHANGED3);
            GppTrace trace = (GppTrace) (this.myGraph.getTrace());
            int graphIndex = this.myGraph.getGraphIndex();
            trace.setBinaryThreadFunction(graphIndex, adapter, this.myGraph.getProfiledFunctions());

            // update the table items and redraw the table
            this.myGraph.getFunctionTable().piEventReceived(be3);
        } else if (be.getType() == PIEvent.CHANGED_FUNCTION_TABLE) {
            // This routine enables all entries in the next table

            int drawMode = this.myGraph.getDrawMode();
            if (drawMode != Defines.BINARIES_FUNCTIONS_THREADS)
                return;

            // we don't need to redraw the binary table, since it has not changed

            PIEvent be3 = new PIEvent(be.getValueObject(), PIEvent.SELECTION_AREA_CHANGED3);
            GppTrace trace = (GppTrace) (this.myGraph.getTrace());
            int graphIndex = this.myGraph.getGraphIndex();
            trace.setFunctionBinaryThread(graphIndex, adapter, this.myGraph.getProfiledThreads());

            // update the table items and redraw the table
            this.myGraph.getThreadTable().piEventReceived(be3);
        }
    }

    public void setSelectedNames() {
        Object[] selectedValues = this.tableViewer.getCheckedElements();
        String[] binaryNames = new String[selectedValues.length];

        for (int i = 0; i < selectedValues.length; i++) {
            if (selectedValues[i] instanceof ProfiledBinary) {
                ProfiledBinary pBinary = (ProfiledBinary) selectedValues[i];
                binaryNames[i] = pBinary.getNameString();
            }
        }

        PIVisualSharedData shared = myGraph.getSharedDataInstance();
        shared.gppSelectedBinaryNames = binaryNames;
    }

    private class SharedCheckHandler implements ICheckStateListener {
        public void checkStateChanged(CheckStateChangedEvent event) {

            if (!(event.getElement() instanceof ProfiledGeneric))
                return;

            // set the stored value to the checkbox value
            ProfiledGeneric pg = (ProfiledGeneric) event.getElement();
            pg.setEnabled(myGraph.getGraphIndex(), event.getChecked());

            // this table's set of checkbox-selected rows has changed, so propagate that information
            setSelectedNames();

            if ((pg.getSampleCount(myGraph.getGraphIndex()) != 0) || (myGraph.getDrawMode() == Defines.BINARIES))
                selectionChangeNotify();

            table.deselectAll();
        }
    }

    private void selectionChangeNotify() {
        PIEvent be = new PIEvent(null, PIEvent.CHANGED_BINARY_TABLE);
        myGraph.piEventReceived(be);
    }

    public void sortOnColumnSelection(TableColumn tableColumn) {
        int columnId = ((Integer) tableColumn.getData()).intValue();
        if (sortColumn == columnId) {
            // sort in other order
            sortAscending = !sortAscending;
        } else {
            // sort in the default order
            switch (columnId) {
            case COLUMN_ID_SHOW:
            case COLUMN_ID_PERCENT_LOAD:
            case COLUMN_ID_SAMPLE_COUNT: {
                // sort in descending order (for checked boxes, this means selected boxes first)
                sortAscending = false;
                break;
            }
            case COLUMN_ID_BINARY:
            case COLUMN_ID_PATH: {
                // sort in descending order
                sortAscending = true;
                break;
            }
            default: {
                return;
            }
            }
        }

        sortColumn = columnId;
        quickSort(sortColumn, profiledBinaries);
    }

    private class ColumnSelectionHandler extends SelectionAdapter {
        @Override
        public void widgetSelected(SelectionEvent e) {
            // wait for the previous sort to finish
            if (sorting || !(e.widget instanceof TableColumn))
                return;

            sortOnColumnSelection((TableColumn) e.widget);
        }
    }

    /**
     * Adds ProfiledGenerics and threshold items to the table data
     * (this.tableItemData and this.profiledThreads). Sorts the ProfiledGenerics
     * according to load; and sets the graph's sorted list accordingly. Refreshes
     * the TableViewer if parameter setInput is true.
     * 
     * @param setInput
     *            if true, reset the TableViewer
     */
    public void updateProfiledAndItemData(boolean setInput) {
        tableItemData.clear();
        profiledBinaries.clear();

        // profiledBinaries and tableItemData contain one entry per table row
        Enumeration<ProfiledGeneric> enu = myGraph.getProfiledBinaries().elements();
        while (enu.hasMoreElements()) {
            ProfiledBinary nextElement = (ProfiledBinary) enu.nextElement();
            tableItemData.add(nextElement);
            profiledBinaries.add(nextElement);
        }

        if (myGraph.getThresholdBinary().getItemCount() != 0) {
            tableItemData.add(myGraph.getThresholdBinary());
            profiledBinaries.add(myGraph.getThresholdBinary());
        }

        // now sort the items in increasing total sample count, so that they graph correctly
        Object[] sorted = myGraph.getProfiledBinaries().toArray();
        Arrays.sort(sorted, new Comparator<Object>() {

            public int compare(Object arg0, Object arg1) {
                if (arg0 instanceof ProfiledGeneric && arg1 instanceof ProfiledGeneric)
                    return (adapter.getTotalSampleCount((ProfiledGeneric) arg0))
                            - (adapter.getTotalSampleCount((ProfiledGeneric) arg1));
                return 0;
            }
        });

        // now create the sorted list used to draw the graph
        myGraph.getSortedBinaries().clear();

        if (myGraph.getThresholdBinary().getItemCount() != 0)
            myGraph.getSortedBinaries().add(myGraph.getThresholdBinary());

        for (int i = 0; i < sorted.length; i++)
            myGraph.getSortedBinaries().add((ProfiledGeneric) sorted[i]);

        // refresh the table, if needed
        if (setInput)
            refreshTableViewer();
    }

    public void quickSort(int sortBy, Vector<ProfiledGeneric> profiled) {
        if (profiled.size() == 0)
            return;

        this.sorting = true;

        ProfiledGeneric pGeneric = profiled.elementAt(profiled.size() - 1);

        if (pGeneric instanceof ProfiledThreshold) {
            profiled.removeElementAt(profiled.size() - 1);
        }

        this.sorter.setupSort(sortBy, this.myGraph.getGraphIndex(), sortAscending);

        switch (sortBy) {
        case COLUMN_ID_SHOW: {
            this.sorter.quickSortByShow(profiled);
            break;
        }
        case COLUMN_ID_PERCENT_LOAD: {
            this.sorter.quickSortByAverageLoad(profiled);
            break;
        }
        case COLUMN_ID_THREAD: {
            this.sorter.quickSortByThread(profiled);
            break;
        }
        case COLUMN_ID_BINARY: {
            this.sorter.quickSortByBinary(profiled);
            break;
        }
        case COLUMN_ID_FUNCTION: {
            this.sorter.quickSortByFunction(profiled);
            break;
        }
        case COLUMN_ID_SAMPLE_COUNT: {
            this.sorter.quickSortBySampleCount(profiled);
            break;
        }
        case COLUMN_ID_PATH: {
            this.sorter.quickSortByBinaryPath(profiled);
            break;
        }
        case COLUMN_ID_FULL_PATH: {
            this.sorter.quickSortByFullBinaryPath(profiled);
            break;
        }
        default: {
            break;
        }
        }

        Enumeration<ProfiledGeneric> e = this.sorter.getSortedList().elements();
        tableItemData = setTableItemData(e);
        if (pGeneric instanceof ProfiledThreshold) {
            tableItemData.add(pGeneric);
            profiled.add(pGeneric);
        }

        // find the column corresponding to sortBy, and give it a column direction
        // NOTE: treat sort by binary path then by binary name as sort by path
        if (sortBy == COLUMN_ID_FULL_PATH)
            sortBy = COLUMN_ID_PATH;
        else if (sortBy == COLUMN_ID_FULL_IN_PATH)
            sortBy = COLUMN_ID_IN_BINARY_PATH;

        TableColumn sortByColumn = null;
        for (int i = 0; i < this.table.getColumnCount(); i++) {
            if (this.table.getColumn(i).getData() instanceof Integer) {
                if (((Integer) this.table.getColumn(i).getData()) == sortBy) {
                    sortByColumn = this.table.getColumn(i);
                    break;
                }
            }
        }

        if (sortByColumn != null) {
            this.table.setSortColumn(sortByColumn);
            this.table.setSortDirection(sortAscending ? SWT.UP : SWT.DOWN);
        }

        refreshTableViewer();
        this.sorting = false;
    }
}