sysperfana/perfinvestigator/com.nokia.carbide.cpp.pi.graphicsmemory/src/com/nokia/carbide/cpp/pi/graphicsmemory/GraphicsMemoryProcessTable.java
author Toni Pulkkinen <ext-toni.p.pulkkinen@nokia.com>
Wed, 23 Jun 2010 15:05:09 +0300
changeset 12 ae255c9aa552
permissions -rw-r--r--
Performance Investigator Carbide extension 2.4.0

/*
 * Copyright (c) 2010 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.graphicsmemory;

import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.awt.event.ActionEvent;
import java.awt.event.FocusEvent;
import java.awt.event.MouseEvent;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.Vector;

import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.ActionContributionItem;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IContributionManager;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.SubMenuManager;
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.CheckboxTableViewer;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.actions.ActionFactory;
import org.eclipse.ui.ide.IIDEActionConstants;

import com.nokia.carbide.cpp.internal.pi.interfaces.ISaveSamples;
import com.nokia.carbide.cpp.internal.pi.visual.GenericTable;
import com.nokia.carbide.cpp.internal.pi.visual.PIEvent;
import com.nokia.carbide.cpp.pi.editors.PIPageEditor;

public class GraphicsMemoryProcessTable extends GenericTable {
	private static final int COLUMN_ID_GRAPHICS_MEMORY_PRIVATE = 14;
	private static final int COLUMN_ID_GRAPHICS_MEMORY_SHARED = 15;
	private static final int COLUMN_ID_GRAPHICS_MEMORY_TOTAL = 16;
	private static final int COLUMN_ID_GRAPHICS_MEMORY_NAME = 17;

	// sort direction
	private boolean sortAscending = true;

	private GraphicsMemoryTraceGraph myGraph;
	private Composite parent;

	// override
	protected Vector<GraphicsMemoryProcess> tableItemData;

	// menu items
	private Action selectAllAction;
	private Action copyTableAction;
	private Action copyAction;
	private Action saveTableAction;

	// class to pass sample data to the save wizard
	public class SaveSampleString implements ISaveSamples {
		boolean done = false;

		public SaveSampleString() {
		}

		public String getData() {
			if (done)
				return null;

			String returnString = getSampleString();
			done = true;
			return returnString;
		}

		public String getData(int size) {
			return getData();
		}

		public int getIndex() {
			return done ? 1 : 0;
		}

		public void clear() {
			done = false;
		}
	}

	/*
	 * return the graphics memory samples selected in the interval
	 */
	protected String getSampleString() {
		int startTime = (int) (PIPageEditor.currentPageEditor().getStartTime() * 1000.0 + 0.0005);
		int endTime = (int) (PIPageEditor.currentPageEditor().getEndTime() * 1000.0 + 0.0005);

		GraphicsMemoryTrace trace = this.myGraph.getMemTrace();
		String returnString = Messages
				.getString("GraphicsMemoryProcessTable.saveSamplesHeading"); //$NON-NLS-1$
		if (trace.getVersion() == 100) {
			TreeMap<Long, ArrayList<GraphicsMemorySample>> sorted = new TreeMap<Long, ArrayList<GraphicsMemorySample>>();
			Enumeration<TreeMap<Long, GraphicsMemorySample>> enume = trace
					.getDrawDataByMemProcess().elements();
			while (enume.hasMoreElements()) {
				TreeMap<Long, GraphicsMemorySample> map = enume.nextElement();
				Iterator<GraphicsMemorySample> iterator = map.values()
						.iterator();
				while (iterator.hasNext()) {
					GraphicsMemorySample memSample = iterator.next();
					if(memSample.process.isEnabled(myGraph.getGraphIndex()) && !memSample.process.fullName
							.equals(GraphicsMemoryTraceParser.SAMPLE_TOTAL_MEMORY_PROCESS_NAME)){
						ArrayList<GraphicsMemorySample> memList = sorted
							.get(memSample.sampleSynchTime);
						if (memList == null) {
							memList = new ArrayList<GraphicsMemorySample>();
						}
						memList.add(memSample);
						sorted.put(memSample.sampleSynchTime, memList);
					}else{
						break;
					}
				}
			}

			SortedMap<Long, ArrayList<GraphicsMemorySample>> selectionAreaMap = sorted
					.subMap((long) startTime, (long) endTime);
			Iterator<ArrayList<GraphicsMemorySample>> iterator = selectionAreaMap
					.values().iterator();

			while (iterator.hasNext()) {
				ArrayList<GraphicsMemorySample> memSamples = iterator.next();
				for (GraphicsMemorySample memSample : memSamples) {			
					returnString += memSample.sampleSynchTime
							+ Messages
									.getString("GraphicsMemoryProcessTable.comma") //$NON-NLS-1$
							+ memSample.process.fullName
							+ Messages
									.getString("GraphicsMemoryProcessTable.comma") //$NON-NLS-1$
							+ ((memSample.privateSize + 512) / 1024)
							+ Messages
									.getString("GraphicsMemoryProcessTable.comma") //$NON-NLS-1$
							+ ((memSample.sharedSize + 512) / 1024) + "\n"; //$NON-NLS-1$		
				}
			}
		}
		return returnString;
	}

	protected MenuItem getSaveSamplesItem(Menu menu, boolean enabled) {
		MenuItem saveSamplesItem = new MenuItem(menu, SWT.PUSH);

		saveSamplesItem.setText(Messages
				.getString("GraphicsMemoryProcessTable.saveCheckedSamples")); //$NON-NLS-1$
		saveSamplesItem.setEnabled(enabled);

		if (enabled) {
			saveSamplesItem.addSelectionListener(new SelectionAdapter() {
				public void widgetSelected(SelectionEvent e) {
					action("saveSamples"); //$NON-NLS-1$
				}
			});
		}

		return saveSamplesItem;
	}

	public GraphicsMemoryProcessTable(GraphicsMemoryTraceGraph myGraph,
			Composite parent) {
		Composite composite = new Composite(parent, SWT.NONE);
		GridLayout gl = new GridLayout();
		gl.marginHeight = 0;
		gl.marginWidth = 0;
		gl.marginLeft = 0;
		gl.marginRight = 0;
		composite.setLayout(gl);
		this.myGraph = myGraph;
		this.parent = composite;

		Label label = new Label(composite, SWT.CENTER);
		label.setBackground(composite.getDisplay().getSystemColor(
				SWT.COLOR_WHITE));
		label.setFont(PIPageEditor.helvetica_8);
		label.setText(Messages.getString("GraphicsMemoryProcessTable.title")); //$NON-NLS-1$

		label.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

		this.tableViewer = CheckboxTableViewer.newCheckList(composite,
				SWT.BORDER | SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL
						| SWT.FULL_SELECTION);
		this.table = this.tableViewer.getTable();
		this.table.setLayoutData(new GridData(GridData.FILL_BOTH));

		// add the check state handler, label provider and content provider
		this.tableViewer.addCheckStateListener(new CheckHandler());
		this.tableViewer.setLabelProvider(new SharedLabelProvider(this.table));
		this.tableViewer.setContentProvider(new MemoryTableContentProvider());
		this.tableViewer.setSorter(new SharedSorter());

		// give the table a heading for possible use in copying and exported
		this.table.setData(Messages
				.getString("GraphicsMemoryProcessTable.memory")); //$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 };
		this.table.setData("isHex", isHex); //$NON-NLS-1$

		// select/deselect column
		column = new TableColumn(this.table, SWT.CENTER);
		column.setText(COLUMN_HEAD_GRAPHICS_MEMORY_NAME);
		column.setWidth(COLUMN_WIDTH_GRAPHICS_MEMORY_NAME); 
		column.setData(Integer.valueOf(COLUMN_ID_GRAPHICS_MEMORY_NAME));
		column.setMoveable(true);
		column.setResizable(true);
		column.addSelectionListener(new ColumnSelectionHandler());

		column = new TableColumn(tableViewer.getTable(), SWT.RIGHT);
		column.setText(COLUMN_HEAD_GRAPHICS_MEMORY_PRIVATE);
		column.setWidth(COLUMN_WIDTH_GRAPHICS_MEMORY_PRIVATE);
		column.setData(Integer.valueOf(COLUMN_ID_GRAPHICS_MEMORY_PRIVATE));
		column.setMoveable(true);
		column.setResizable(true);
		column.addSelectionListener(new ColumnSelectionHandler());

		column = new TableColumn(tableViewer.getTable(), SWT.RIGHT);
		column.setText(COLUMN_HEAD_GRAPHICS_MEMORY_SHARED);
		column.setWidth(COLUMN_WIDTH_GRAPHICS_MEMORY_SHARED);
		column.setData(Integer.valueOf(COLUMN_ID_GRAPHICS_MEMORY_SHARED));
		column.setMoveable(true);
		column.setResizable(true);
		column.addSelectionListener(new ColumnSelectionHandler());

		column = new TableColumn(tableViewer.getTable(), SWT.RIGHT);
		column.setText(COLUMN_HEAD_MEMORY_TOTAL);
		column.setWidth(COLUMN_WIDTH_MEMORY_TOTAL);
		column.setData(Integer.valueOf(COLUMN_ID_GRAPHICS_MEMORY_TOTAL));
		column.setMoveable(true);
		column.setResizable(true);
		column.addSelectionListener(new ColumnSelectionHandler());

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

		this.table.addMouseListener(new TableMouseListener());
		this.table.setHeaderVisible(true);
		this.table.setLinesVisible(true);
		this.table.setRedraw(true);

		updateItemData(true);
		((SharedSorter) tableViewer.getSorter())
				.doSort(COLUMN_ID_GRAPHICS_MEMORY_NAME);

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

		createDefaultActions();

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

		table.addFocusListener(new FocusListener() {
			IAction oldSelectAllAction = null;
			IAction oldCopyAction = null;

			public void focusGained(org.eclipse.swt.events.FocusEvent arg0) {
				IActionBars bars = PIPageEditor.getActionBars();

				oldSelectAllAction = PIPageEditor.getActionBars()
						.getGlobalActionHandler(
								ActionFactory.SELECT_ALL.getId());
				oldCopyAction = PIPageEditor.getActionBars()
						.getGlobalActionHandler(ActionFactory.COPY.getId());

				bars.setGlobalActionHandler(ActionFactory.COPY.getId(),
						copyAction);
				bars.setGlobalActionHandler(ActionFactory.SELECT_ALL.getId(),
						selectAllAction);

				copyAction.setEnabled(table.getSelectionCount() > 0);
				selectAllAction.setEnabled(table.getItemCount() > 0);
				bars.updateActionBars();

				// add to the Edit menu
				IMenuManager editMenuManager = bars.getMenuManager()
						.findMenuUsingPath(IIDEActionConstants.M_EDIT);

				if (editMenuManager instanceof SubMenuManager) {
					IContributionManager editManager = ((SubMenuManager) editMenuManager)
							.getParent();
					ActionContributionItem item;

					editMenuManager.remove("PICopyTable"); //$NON-NLS-1$
					copyTableAction.setEnabled(table.getItemCount() > 0);
					item = new ActionContributionItem(copyTableAction);
					item.setVisible(true);
					editManager.prependToGroup(IIDEActionConstants.CUT_EXT,
							item);
				}

				// add to the File menu
				IMenuManager fileMenuManager = bars.getMenuManager()
						.findMenuUsingPath(IIDEActionConstants.M_FILE);

				if (fileMenuManager instanceof SubMenuManager) {
					IContributionManager fileManager = ((SubMenuManager) fileMenuManager)
							.getParent();
					ActionContributionItem item;

					fileMenuManager.remove("PISaveTable"); //$NON-NLS-1$
					saveTableAction.setEnabled(table.getItemCount() > 0);
					item = new ActionContributionItem(saveTableAction);
					item.setVisible(true);
					fileManager.insertAfter("saveAll", item); //$NON-NLS-1$
				}
			}

			public void focusLost(org.eclipse.swt.events.FocusEvent arg0) {
				IActionBars bars = PIPageEditor.getActionBars();
				bars.setGlobalActionHandler(ActionFactory.COPY.getId(),
						oldCopyAction);
				bars.setGlobalActionHandler(ActionFactory.SELECT_ALL.getId(),
						oldSelectAllAction);
				bars.updateActionBars();

				SubMenuManager editMenuManager = (SubMenuManager) PIPageEditor
						.getMenuManager().find(IIDEActionConstants.M_EDIT);
				editMenuManager.remove("PICopyTable"); //$NON-NLS-1$
				editMenuManager.update();

				SubMenuManager fileMenuManager = (SubMenuManager) PIPageEditor
						.getMenuManager().find(IIDEActionConstants.M_FILE);
				fileMenuManager.remove("PISaveTable"); //$NON-NLS-1$
				fileMenuManager.update();
			}
		});

		tableViewer.refresh();
		table.redraw();
	}

	private class TableMouseListener implements MouseListener {
		public void mouseDoubleClick(org.eclipse.swt.events.MouseEvent e) {
			if (e.button == MouseEvent.BUTTON1) {
				TableItem[] selectedItems = table.getSelection();
				if (selectedItems.length == 0)
					return;

				if (selectedItems[0].getData() instanceof GraphicsMemoryProcess) {
					GraphicsMemoryProcess memProcess = (GraphicsMemoryProcess) (selectedItems[0]
							.getData());
					if (memProcess.isEnabled(myGraph.getGraphIndex()))
						action("remove"); //$NON-NLS-1$
					else
						action("add"); //$NON-NLS-1$
				}
			}
			selectAllAction.setEnabled(table.getItemCount() > 0);
			copyAction.setEnabled(table.getSelectionCount() > 0);
			copyTableAction.setEnabled(table.getItemCount() > 0);
		}

		public void mouseDown(org.eclipse.swt.events.MouseEvent e) {
		}

		public void mouseUp(org.eclipse.swt.events.MouseEvent e) {

			selectAllAction.setEnabled(table.getItemCount() > 0);
			copyAction.setEnabled(table.getSelectionCount() > 0);
			copyTableAction.setEnabled(table.getItemCount() > 0);

			if (e.button == MouseEvent.BUTTON3) {
				// get rid of last Menu created so we don't have double menu
				// on click
				if (contextMenu != null) {
					contextMenu.dispose();
				}

				contextMenu = new Menu(table.getShell(), SWT.POP_UP);
				getCheckRows(contextMenu, table.getSelectionCount() > 0);

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

				// save all
				new MenuItem(contextMenu, SWT.SEPARATOR);
				getSaveTableItem(contextMenu, table.getItemCount() > 0);
				saveTableAction.setEnabled(table.getItemCount() > 0);

				// save samples
				int startTime = (int) (PIPageEditor.currentPageEditor()
						.getStartTime() * 1000.0f);
				int endTime = (int) (PIPageEditor.currentPageEditor()
						.getEndTime() * 1000.0f);

				getSaveSamplesItem(contextMenu, myGraph.haveEnabled()
						&& (startTime != -1) && (endTime != -1)
						&& (startTime != endTime));

				contextMenu.setLocation(parent.toDisplay(e.x
						+ table.getLocation().x, e.y + table.getLocation().y));
				contextMenu.setVisible(true);

				new MenuItem(contextMenu, SWT.SEPARATOR);

				MenuItem memoryStatsItem = new MenuItem(contextMenu, SWT.PUSH);
				memoryStatsItem.setText(Messages
						.getString("GraphicsMemoryPlugin.memoryStats")); //$NON-NLS-1$
				memoryStatsItem.addSelectionListener(new SelectionAdapter() {
					public void widgetSelected(SelectionEvent e) {
						new GraphicsMemoryStatisticsDialog(Display.getCurrent());
					}
				});

				table.setMenu(contextMenu);
			}
		}
	}

	private static class MemoryTableContentProvider implements
			IStructuredContentProvider {

		public MemoryTableContentProvider() {
			super();
		}

		@SuppressWarnings("unchecked")
		public Object[] getElements(Object inputElement) {
			return ((Vector<GraphicsMemoryProcess>) inputElement).toArray();
		}

		public void dispose() {
		}

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

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

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

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

			selectionChangeNotify();

			table.deselectAll();
		}
	}

	void selectionChangeNotify() {
		this.tableViewer.refresh();
		this.table.redraw();
		PIEvent be = new PIEvent(null, PIEvent.CHANGED_MEMORY_TABLE);
		myGraph.piEventReceived(be);
	}

	private class SharedLabelProvider extends LabelProvider implements
			ITableLabelProvider {

		private DecimalFormat decimalFormat = new DecimalFormat(Messages
				.getString("GraphicsMemoryProcessTable.kbFormat")); //$NON-NLS-1$

		Table table;

		public SharedLabelProvider(Table table) {
			super();
			this.table = table;
		}

		public String getColumnText(Object element, int columnIndex) {
			int columnId = ((Integer) table.getColumn(columnIndex).getData())
					.intValue();

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

			GraphicsMemoryProcess profiledItem = (GraphicsMemoryProcess) element;

			switch (columnId) {
			case COLUMN_ID_GRAPHICS_MEMORY_NAME: {
				return profiledItem.fullName;
			}
			case COLUMN_ID_GRAPHICS_MEMORY_PRIVATE: {
				double startTime = PIPageEditor.currentPageEditor()
						.getStartTime();
				double endTime = PIPageEditor.currentPageEditor().getEndTime();

				if ((startTime == -1) || (endTime == -1)
						|| (startTime == endTime))
					return ""; //$NON-NLS-1$

				return decimalFormat
						.format((profiledItem.maxMemoryItem.maxPrivate + 512) / 1024);
			}
			case COLUMN_ID_GRAPHICS_MEMORY_SHARED: {
				double startTime = PIPageEditor.currentPageEditor()
						.getStartTime();
				double endTime = PIPageEditor.currentPageEditor().getEndTime();

				if ((startTime == -1) || (endTime == -1)
						|| (startTime == endTime))
					return ""; //$NON-NLS-1$

				return decimalFormat
						.format((profiledItem.maxMemoryItem.maxShared + 512) / 1024);
			}
			case COLUMN_ID_GRAPHICS_MEMORY_TOTAL: {
				double startTime = PIPageEditor.currentPageEditor()
						.getStartTime();
				double endTime = PIPageEditor.currentPageEditor().getEndTime();

				if ((startTime == -1) || (endTime == -1)
						|| (startTime == endTime))
					return ""; //$NON-NLS-1$

				return decimalFormat
						.format((profiledItem.maxMemoryItem.maxTotal + 512) / 1024);
			}
			default: {
				break;
			}
			}
			// should never get here
			return ""; //$NON-NLS-1$
		}

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

	/*
	 * TableViewer sorter for the called-by and called function tableviewers
	 */
	private class SharedSorter extends ViewerSorter {
		// last column sorted
		private int column = -1;

		/*
		 * decide on which column to sort by, and the sort ordering
		 */
		public void doSort(int column) {
			// ignore the column passed in and use the id set by the column
			// selection handler
			if (column == this.column) {
				// sort in other order
				sortAscending = !sortAscending;
			} else {
				// changed columns, so sort in the default order
				switch (column) {
				case COLUMN_ID_GRAPHICS_MEMORY_NAME: {
					// sort in ascending order
					sortAscending = true;
					break;
				}
				case COLUMN_ID_GRAPHICS_MEMORY_PRIVATE:
				case COLUMN_ID_GRAPHICS_MEMORY_SHARED:
				case COLUMN_ID_GRAPHICS_MEMORY_TOTAL: {
					// sort in descending order
					sortAscending = false;
					break;
				}
				default: {
					// ignore the column
					return;
				}
				}
				this.column = column;
			}

			// find the TableColumn corresponding to column, and give it a
			// column direction
			TableColumn sortByColumn = null;
			for (int i = 0; i < table.getColumnCount(); i++) {
				if (table.getColumn(i).getData() instanceof Integer) {
					if (((Integer) table.getColumn(i).getData()) == column) {
						sortByColumn = table.getColumn(i);
						break;
					}
				}
			}

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

		/*
		 * compare two items from a table column
		 */
		@SuppressWarnings("unchecked")
		public int compare(Viewer viewer, Object e1, Object e2) {
			int returnCode = 0;

			GraphicsMemoryProcess elem1 = (GraphicsMemoryProcess) e1;
			GraphicsMemoryProcess elem2 = (GraphicsMemoryProcess) e2;

			// find the memory information for the two processes

			// compare based on the memory information
			switch (column) {
			case COLUMN_ID_GRAPHICS_MEMORY_PRIVATE:

				returnCode = elem1.maxMemoryItem.maxPrivate > elem2.maxMemoryItem.maxPrivate ? 1
						: -1;
				break;
			case COLUMN_ID_GRAPHICS_MEMORY_SHARED:
				returnCode = elem1.maxMemoryItem.maxShared > elem2.maxMemoryItem.maxShared ? 1
						: -1;
				break;
			case COLUMN_ID_GRAPHICS_MEMORY_TOTAL:
				returnCode = elem1.maxMemoryItem.maxTotal > elem2.maxMemoryItem.maxTotal ? 1
						: -1;
				break;
			case COLUMN_ID_GRAPHICS_MEMORY_NAME: {
				returnCode = this.getComparator().compare(elem1.fullName,
						elem2.fullName);
				break;
			}
			default:
				break;
			}

			// for descending order, reverse the sense of the compare
			if (!sortAscending)
				returnCode = -returnCode;

			return returnCode;
		}
	}

	public void actionPerformed(ActionEvent e) {
	}

	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);
		} else if (actionString.equals("addall") //$NON-NLS-1$
				|| actionString.equals("removeall")) //$NON-NLS-1$
		{
			actionAddRemoveAll(actionString, graphIndex);
		} else if (actionString.equals("copy")) //$NON-NLS-1$
		{
			actionCopyOrSave(true, this.table, CHECKBOX_TEXT, false, "\t", "\n"); //$NON-NLS-1$ //$NON-NLS-2$
			return; // no redraw needed
		} else if (actionString.equals("copyTable")) //$NON-NLS-1$
		{
			actionCopyOrSave(true, this.table, CHECKBOX_TEXT, true, "\t", "\n"); //$NON-NLS-1$ //$NON-NLS-2$
			return; // no redraw needed
		} else if (actionString.equals("selectAll")) //$NON-NLS-1$
		{
			actionSelectAll();
			return;
		} else if (actionString.equals("saveTable")) //$NON-NLS-1$
		{
			actionCopyOrSave(false, this.table, CHECKBOX_TEXT, true, ",", "\n"); //$NON-NLS-1$ //$NON-NLS-2$
			return; // no redraw needed
		} else if (actionString.equals("saveSamples")) //$NON-NLS-1$
		{
			SaveSampleString saveSampleString = new SaveSampleString();
			actionSaveSamples(saveSampleString); //$NON-NLS-1$
			return;
		} 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_TEXT, ",", "\n"); //$NON-NLS-1$ //$NON-NLS-2$
			String copyString = getString.getData();
			StringSelection contents = new StringSelection(copyString);
			cb.setContents(contents, contents);
			return;
		}

		tableViewer.refresh();
		table.redraw();
		this.myGraph.repaint();
	}

	private void actionAddRemove(String actionString, int graphIndex) {
		GraphicsMemoryProcess memProcess;

		// 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);
			memProcess = (GraphicsMemoryProcess) ((TableItem) selectedItems[i])
					.getData();
			memProcess.setEnabled(graphIndex, addIt);
		}

		// this table's set of checkbox-selected rows has changed,
		// so propagate that information
		if (selectedItems.length != 0)
			selectionChangeNotify();

		this.table.deselectAll();
	}

	private void actionAddRemoveAll(String actionString, int graphIndex) {
		GraphicsMemoryProcess memProcess;

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

		TableItem[] selectedItems = this.table.getItems();
		for (int i = 0; i < selectedItems.length; i++) {
			selectedItems[i].setChecked(addIt);
			memProcess = (GraphicsMemoryProcess) ((TableItem) selectedItems[i])
					.getData();
			memProcess.setEnabled(graphIndex, addIt);
		}

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

		this.table.deselectAll();
	}

	public void focusGained(FocusEvent e) {
	}

	public void focusLost(FocusEvent e) {
	}

	public void piEventReceived(PIEvent be) {
		if (be.getType() == PIEvent.SELECTION_AREA_CHANGED2) {
			this.tableViewer.refresh();
			this.table.redraw();
		}
	}

	public void updateItemData(boolean setInput) {
		HashSet<GraphicsMemoryProcess> noDuplicateMemProcesses = myGraph
				.getMemTrace().getNoDuplicateMemProcesses();
		if (tableItemData == null)
			tableItemData = new Vector<GraphicsMemoryProcess>(
					noDuplicateMemProcesses.size());
		else
			tableItemData.clear();

		// tableItemData contains one entry per table row
		// the first profiled process is the system-wide total memory information
		for (Iterator<GraphicsMemoryProcess> iter = noDuplicateMemProcesses
				.iterator(); iter.hasNext();) {
			GraphicsMemoryProcess memProcess = iter.next();
			if ((memProcess.processId != GraphicsMemoryTraceParser.SAMPLE_TOTAL_MEMORY_PROCESS_ID)) {
				tableItemData.add(memProcess);
			}
		}

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

	public void refreshTableViewer() {
		this.tableViewer.setInput(tableItemData);
	}

	public void sortOnColumnSelection(TableColumn tableColumn) {
		int columnID = ((Integer) tableColumn.getData()).intValue();
		((SharedSorter) tableViewer.getSorter()).doSort(columnID);

		tableViewer.refresh();
		table.redraw();
	}

	private class ColumnSelectionHandler extends SelectionAdapter {
		public void widgetSelected(SelectionEvent e) {
			if (!(e.widget instanceof TableColumn))
				return;

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

	public CheckboxTableViewer getTableViewer() {
		return this.tableViewer;
	}

	protected void createDefaultActions() {
		selectAllAction = new Action("SelectAll") { //$NON-NLS-1$
			public void run() {
				action("selectAll"); //$NON-NLS-1$
			}
		};
		selectAllAction.setEnabled(true);

		copyAction = new Action("Copy") { //$NON-NLS-1$
			public void run() {
				action("copy"); //$NON-NLS-1$
			}
		};
		copyAction.setEnabled(false);

		copyTableAction = new Action("CopyTable") { //$NON-NLS-1$
			public void run() {
				action("copyTable"); //$NON-NLS-1$
			}
		};
		copyTableAction.setEnabled(true);
		copyTableAction.setId("PICopyTable"); //$NON-NLS-1$
		copyTableAction.setText(Messages
				.getString("GraphicsMemoryProcessTable.copyTable")); //$NON-NLS-1$

		saveTableAction = new Action("SaveTable") { //$NON-NLS-1$
			public void run() {
				action("saveTable"); //$NON-NLS-1$
			}
		};
		saveTableAction.setEnabled(true);
		saveTableAction.setId("PISaveTable"); //$NON-NLS-1$
		saveTableAction.setText(Messages
				.getString("GraphicsMemoryProcessTable.SaveTable")); //$NON-NLS-1$
	}
}