sysperfana/memspyext/com.nokia.s60tools.memspy/src/com/nokia/s60tools/memspy/ui/dialogs/SWMTCategoriesDialog.java
/*
* 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 "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.s60tools.memspy.ui.dialogs;
import java.util.ArrayList;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.dialogs.TitleAreaDialog;
import org.eclipse.jface.viewers.CheckboxTableViewer;
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.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.PlatformUI;
import com.nokia.s60tools.memspy.model.SWMTCategoryConstants;
import com.nokia.s60tools.memspy.model.SWMTCategorys;
import com.nokia.s60tools.memspy.preferences.MemSpyPreferences;
import com.nokia.s60tools.memspy.resources.HelpContextIDs;
import com.nokia.s60tools.memspy.resources.ImageKeys;
import com.nokia.s60tools.memspy.resources.ImageResourceManager;
import com.nokia.s60tools.memspy.util.MemSpyConsole;
import com.nokia.s60tools.ui.S60ToolsTable;
import com.nokia.s60tools.ui.S60ToolsTableColumnData;
import com.nokia.s60tools.ui.S60ToolsTableFactory;
import com.nokia.s60tools.ui.S60ToolsUIConstants;
/**
* Dialog for selecting SWMT categories to be tracked.
*/
public class SWMTCategoriesDialog extends TitleAreaDialog{
//
// Private classes
//
private static final String HEAP_DATA_THREAD_FILTER_TXT = "Heap Data Thread Filter";
/**
* Label provider for table viewer component.
*/
class SWMTCategoryViewerLabelProvider extends LabelProvider implements ITableLabelProvider{
/* (non-Javadoc)
* @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnImage(java.lang.Object, int)
*/
public Image getColumnImage(Object element, int columnIndex) {
return null; // No images used
}
/* (non-Javadoc)
* @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnText(java.lang.Object, int)
*/
public String getColumnText(Object element, int columnIndex) {
String label = element.toString();
SWMTCategoryEntry entryData = (SWMTCategoryEntry) element;
switch (columnIndex) {
case SWMTCategoryEntry.NAME_COLUMN_INDEX:
label = entryData.getCategoryName();
break;
default:
MemSpyConsole.getInstance().println("Unexpected column index: " + columnIndex, MemSpyConsole.MSG_ERROR); //$NON-NLS-1$ //$NON-NLS-2$
break;
}
return label;
}
}
//
// Private constants
//
/**
* Columns in the container area.
*/
private static final int COLUMN_COUNT = 1;
/**
* Dialog width.
*/
private static final int DIALOG_WIDTH = 425;
/**
* Dialog height.
*/
private static final int DIALOG_HEIGHT = 550;
/**
* Percentage as decimal number how much table viewer is taking space horizontally.
*/
private static final double TABLE_VIEWER_WIDTH_PERCENTAGE = 0.8;
/**
* Default guiding message shown to the user in add new mode.
*/
private static final String DEFAULT_MESSAGE = "Set advanced options and tracked SWMT categories.";
/**
* Complete message shown to the user in add new mode.
*/
private static final String COMPLETE_MESSAGE = "Press OK to save advanced options and set categories to be tracked";
/**
* Error message shown to the user in case no categories are selected.
*/
private static final String ERROR_MESSAGE = "At least single category must be selected";
/**
* UI text for closing Symbian agent
*/
private static final String CLOSE_MEM_SPY_BETWEEN_CYCLES_TEXT = "Close MemSpy Symbian Agent Between Cycles";
/**
* Tip text for closing Symbian agent
*/
private static final String CLOSE_MEM_SPY_BETWEEN_CYCLES_TIP_TEXT = "Choose if the MemSpy Symbian agent in S60 target is closed between cycles. That is more realistic use case for memory usage point of view.";
//
// Member data
//
/**
* Flag used to make sure that create() and open() are called in correct order.
*/
private boolean isCreateCalled = false;
//
// UI Controls
//
/**
* Container area for individual fields for the user for entering information.
*/
private Composite container;
/**
* Reference to OK button that can be disabled/enabled
* due to current category selection-
*/
private Button okActionBtn;
/**
* Viewer showing currently selected category file entries.
*/
private CheckboxTableViewer categoryViewer;
/**
* Selected categories as a result of bitwise OR.
*/
private int categorySelection = SWMTCategoryConstants.CATEGORY_ALL;
//User Heap filter text field
private Text heapText;
private Button closeBetweenCyclesButton;
private Button heapDumpBtn;
/**
* Constructor. Used to open dialog in order to add new entry.
* @param parentShell Parent shell for the dialog.
* @param categorySelection integer containing initially selected categories as a result of bitwise OR.
*/
public SWMTCategoriesDialog(Shell parentShell, int categorySelection) {
super(parentShell);
this.categorySelection = categorySelection;
// Setting banner image
String bannerImage = ImageKeys.IMG_WIZARD;
setTitleImage(ImageResourceManager.getImage(bannerImage));
}
/* (non-Javadoc)
* @see org.eclipse.jface.dialogs.Dialog#createButtonsForButtonBar(org.eclipse.swt.widgets.Composite)
*/
protected void createButtonsForButtonBar(Composite parent) {
createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL,
true);
createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL,
true);
okActionBtn = getButton(IDialogConstants.OK_ID);
}
/* (non-Javadoc)
* @see org.eclipse.jface.window.Window#configureShell(org.eclipse.swt.widgets.Shell)
*/
protected void configureShell(Shell shell) {
super.configureShell(shell);
shell.setText("SWMT Categories and Advanced Options");
}
/* (non-Javadoc)
* @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite)
*/
protected Control createDialogArea(Composite parent) {
Composite dialogAreaComposite = (Composite) super.createDialogArea(parent);
//
// Creating container and layout for it
//
container = new Composite(dialogAreaComposite, SWT.NONE);
GridLayout gdl = new GridLayout(COLUMN_COUNT, false);
// Settings margins according Carbide branding guideline
gdl.marginLeft = S60ToolsUIConstants.MARGIN_BTW_FRAME_AND_CONTENTS;
gdl.marginRight = S60ToolsUIConstants.MARGIN_BTW_FRAME_AND_CONTENTS;
gdl.marginTop = S60ToolsUIConstants.MARGIN_BTW_FRAME_AND_CONTENTS;
gdl.marginBottom = S60ToolsUIConstants.MARGIN_BTW_FRAME_AND_CONTENTS;
container.setLayout(gdl);
container.setLayoutData(new GridData(GridData.FILL_BOTH));
//
// Symbian agent options group
//
createSymbianAgentOptionsGroup();
//
// Head dump group
//
createHeapDumpGroup();
//
// Creating table viewer for showing category file entries
//
categoryViewer = createCategoryCheckBoxTableViewer(container);
GridData categoryViewerGd = new GridData(GridData.FILL_BOTH);
// Spanning as many rows as there are actions buttons on the right
categoryViewerGd.verticalSpan = 3;
categoryViewerGd.widthHint = (int) (TABLE_VIEWER_WIDTH_PERCENTAGE * DIALOG_WIDTH);
categoryViewer.getControl().setLayoutData(categoryViewerGd);
categoryViewer.setSorter(new SWMTCategoryEntryTableViewerSorter());
// Adding selection change listener
categoryViewer.addSelectionChangedListener(new ISelectionChangedListener(){
/* (non-Javadoc)
* @see org.eclipse.jface.viewers.ISelectionChangedListener#selectionChanged(org.eclipse.jface.viewers.SelectionChangedEvent)
*/
public void selectionChanged(SelectionChangedEvent event) {
notifySelectionChanged();
}
});
//
// Setting providers for table viewer
//
// Creating content provider
GategoryProvider categoryViewerContentProvider = new GategoryProvider();
// Setting content provider
categoryViewer.setContentProvider(categoryViewerContentProvider);
categoryViewer.setInput(categoryViewerContentProvider);
// Label provider
categoryViewer.setLabelProvider(new SWMTCategoryViewerLabelProvider());
// Setting initial category selection state
InitializeSelectionState();
// Setting context-sensitive help ID
PlatformUI.getWorkbench().getHelpSystem().setHelp( dialogAreaComposite, HelpContextIDs.MEMSPY_IMPORT_SWMT_CATEGORIES_DIALOG);
// Dialog are composite ready
return dialogAreaComposite;
}
/**
* Private class to provide content for category viewer
*/
class GategoryProvider implements IStructuredContentProvider {
/* (non-Javadoc)
* @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)
*/
public Object[] getElements(Object inputElement) {
return SWMTCategorys.getInstance().getCategoryEntries().toArray();
}
/* (non-Javadoc)
* @see org.eclipse.jface.viewers.IContentProvider#dispose()
*/
public void dispose() {
// Not needed but needs to be implemented
}
public void inputChanged(Viewer viewer, Object oldInput,
Object newInput) {
// Not used but needs to be implemented
}
};
/**
* Creates group for Symbian agent options
*/
private void createSymbianAgentOptionsGroup() {
Group closeSymbianGroup = new Group(container, SWT.SHADOW_NONE);
closeSymbianGroup.setText("Symbian Agent Options");
GridLayout closegl = new GridLayout(2, false);
GridData closegd = new GridData(GridData.FILL_BOTH);
closeSymbianGroup.setLayout(closegl);
closeSymbianGroup.setLayoutData(closegd);
closeBetweenCyclesButton = new Button(closeSymbianGroup, SWT.CHECK);
GridData closeLayoutData = new GridData(GridData.VERTICAL_ALIGN_END);
closeBetweenCyclesButton.setLayoutData( closeLayoutData);
closeBetweenCyclesButton.setToolTipText(CLOSE_MEM_SPY_BETWEEN_CYCLES_TIP_TEXT);
boolean isToBeClosedBetweenCycles = MemSpyPreferences.isCloseSymbianAgentBetweenCyclesSelected();
closeBetweenCyclesButton.setSelection(isToBeClosedBetweenCycles);
//Label for button, separate label is used because setting text to button wont align text to same vertical
//position than next label.
Label closeBetweenIntervalsLabel = new Label(closeSymbianGroup, SWT.LEFT );
GridData closeBetweenLabelData = new GridData(GridData.VERTICAL_ALIGN_CENTER);
closeBetweenIntervalsLabel.setText( CLOSE_MEM_SPY_BETWEEN_CYCLES_TEXT );
closeBetweenIntervalsLabel.setLayoutData(closeBetweenLabelData);
}
/**
* Creates group for Head Dumps
*/
private void createHeapDumpGroup() {
//
// Head dump group
//
Group headDumpGroup = new Group(container, SWT.SHADOW_NONE);
headDumpGroup.setText("Heap Dumps During SWMT Logging");
GridLayout hdgl = new GridLayout(2, false);
GridData hdgd = new GridData(GridData.FILL_BOTH);
headDumpGroup.setLayout(hdgl);
headDumpGroup.setLayoutData(hdgd);
heapDumpBtn = new Button(headDumpGroup, SWT.CHECK);
heapDumpBtn.setText("Get Heap Dumps for Threads to Analyse with Heap Analyser");
heapDumpBtn.setToolTipText("Set if Heap Dumps is to be received for Threads during SWMT logging");
heapDumpBtn.addSelectionListener(new HeadDumpSelectionListener());
heapDumpBtn.setSelection(MemSpyPreferences.isSWMTHeapDumpSelected());
GridData hdBtnGd = new GridData();
hdBtnGd.horizontalSpan = 2;
heapDumpBtn.setLayoutData(hdBtnGd);
//Label for Heap gategory limit
Label heapTextLabel = new Label(headDumpGroup, SWT.LEFT );
heapTextLabel.setText(HEAP_DATA_THREAD_FILTER_TXT + ":");
String heapFilterToolTipText = "When filter string is specified, only Threads that contain text specified will be tracked.";
heapTextLabel.setToolTipText(heapFilterToolTipText);
GridData heapTextLimitLayoutData = new GridData(GridData.VERTICAL_ALIGN_CENTER);
heapTextLabel.setLayoutData( heapTextLimitLayoutData );
//heap limit text
heapText = new Text(headDumpGroup, SWT.LEFT | SWT.BORDER | SWT.BORDER);
heapText.setTextLimit(120);//Text limit 128 specified in S60 side
GridData heapTextLayoutData = new GridData(GridData.VERTICAL_ALIGN_CENTER);
heapTextLayoutData.widthHint = 140;
heapText.setLayoutData( heapTextLayoutData );
heapText.setToolTipText(heapFilterToolTipText);
if(heapDumpBtn.getSelection()){
heapText.setText(MemSpyPreferences.getSWMTHeapNameFilter());
}else{
heapText.setEnabled(false);
}
}
/**
* Listener for Heap Dumps button, updates preferences and text in Heap Dumps group.
*/
private class HeadDumpSelectionListener implements SelectionListener{
/* (non-Javadoc)
* @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent)
*/
public void widgetDefaultSelected(SelectionEvent e) {
// not needed
}
/*
* Change heapText and save preferences
* (non-Javadoc)
* @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent)
*/
public void widgetSelected(SelectionEvent e) {
boolean selection = heapDumpBtn.getSelection();
if(selection){
heapText.setEnabled(true);
heapText.setText(MemSpyPreferences.getSWMTHeapNameFilter());
}else{
heapText.setText("");
heapText.setEnabled(false);
}
}
}
/**
* Save the heap filter text if enabled
*/
private void saveHeapFilterText() {
//Check if heaptext is enabled, if it is, saving text to preferences
if(heapText.isEnabled()){
String heapFilterText = heapText.getText().trim();
MemSpyPreferences.setSWMTHeapNameFilter(heapFilterText);
}
}
/**
* Save the Close between cycles selection
*/
private void saveCloseS60AgentBetweenCycles() {
boolean selection = closeBetweenCyclesButton.getSelection();
// User selects to close MemSpy S60 application between cycles
MemSpyPreferences.setCloseSymbianAgentBetweenCycles(selection);
}
/**
* Save the heap dump selection
*/
private void saveHeapDumpSelection() {
boolean selection = heapDumpBtn.getSelection();
MemSpyPreferences.setSWMTHeapDumpSelected(selection);
}
/**
* Sets initial selection state for entries
*/
private void InitializeSelectionState() {
int size = SWMTCategorys.getInstance().getCategoryEntries().size();
for (int i = 0; i < size; i++) {
SWMTCategoryEntry entry = (SWMTCategoryEntry) categoryViewer.getElementAt(i);
int isCategoryBitUp = categorySelection & entry.getCategoryId();
if(isCategoryBitUp != 0){
categoryViewer.setChecked(entry, true);
}
}
}
/* (non-Javadoc)
* @see org.eclipse.jface.dialogs.TitleAreaDialog#getInitialSize()
*/
protected Point getInitialSize() {
return new Point(DIALOG_WIDTH, DIALOG_HEIGHT);
}
/* (non-Javadoc)
* @see org.eclipse.jface.dialogs.Dialog#create()
*/
public void create() {
super.create();
// Currently just does creation by super call and stores status
isCreateCalled = true;
}
/* (non-Javadoc)
* @see org.eclipse.jface.dialogs.Dialog#okPressed()
*/
protected void okPressed() {
if(heapDumpBtn.getSelection()){
if(heapText.getText().trim().equals("")){
System.out.println("DEBUG: WARNING! Empty heap filter text!");
MessageBox box = new MessageBox(getShell(),SWT.ICON_WARNING | SWT.YES | SWT.NO);
String warningMsg = "Empty \"" +HEAP_DATA_THREAD_FILTER_TXT
+ "\" field causes huge amount of data to be traced and tracing may take a very long time. "
+"It is highly recommended to use \"" +HEAP_DATA_THREAD_FILTER_TXT
+ "\"."
+"\n\nDo you want to continue anyway?";
box.setMessage(warningMsg);
box.setText("Using \"" +HEAP_DATA_THREAD_FILTER_TXT
+ "\" is recommended");
int yes_no = box.open();
//If user does not want to continue but cancel, returning, otherwise just continue to save all data and exit
if(yes_no == SWT.NO){
return;
}
}
}
saveHeapFilterText();
saveCloseS60AgentBetweenCycles();
saveHeapDumpSelection();
super.close();
}
/* (non-Javadoc)
* @see org.eclipse.jface.window.Window#open()
*/
public int open(){
try {
// Making sure that create is called
if(!isCreateCalled){
create();
}
showDefaultMessage();
} catch (Exception e) {
e.printStackTrace();
}
return super.open();
}
/**
* Resets possible error messages and show the default message.
*/
private void showDefaultMessage() {
setErrorMessage(null);
setMessage(DEFAULT_MESSAGE, IMessageProvider.INFORMATION);
}
/**
* Informs user that parameters are valid and dialog can be
* dismissed with OK button..
*/
private void setCompleteOkMessage() {
setErrorMessage(null);
setMessage(COMPLETE_MESSAGE, IMessageProvider.INFORMATION);
}
/**
* Shows error message in case no categories are selected.
*/
private void showErrorMessage() {
setErrorMessage(ERROR_MESSAGE);
setMessage(null);
}
/**
* Disables OK button.
* This method is guarded against call during construction
* when button row has not been created yet and widget is <code>null</code>.
*/
private void disableOk() {
if(okActionBtn != null){
okActionBtn.setEnabled(false);
}
}
/**
* Enables OK button.
* This method is guarded against call during construction
* when button row has not been created yet and widget is <code>null</code>.
*/
private void enableOk() {
if(okActionBtn != null){
okActionBtn.setEnabled(true);
}
}
/**
* Updates buttons statuses according the current selection
* status of table entry viewer contents and refreshes contents.
*/
private void notifySelectionChanged() {
Object[] selectedCategoryEntries = getSelectedCategoryEntries();
if(selectedCategoryEntries.length > 0){
updateSelectedCategories(selectedCategoryEntries);
enableOk();
setCompleteOkMessage();
}
else{
disableOk();
showErrorMessage();
}
categoryViewer.refresh();
}
/**
* Creates checkbox viewer component for showing available SWMT categories.
* @param parent Parent composite for the created composite.
* @return New <code>CheckboxTableViewer</code> object instance.
*/
protected CheckboxTableViewer createCategoryCheckBoxTableViewer(Composite parent) {
ArrayList<S60ToolsTableColumnData> columnDataArr = new ArrayList<S60ToolsTableColumnData>();
//
// NOTE: Column indices must start from zero (0) and
// the columns must be added in ascending numeric
// order.
//
columnDataArr.add(new S60ToolsTableColumnData("Category", //$NON-NLS-1$
350,
SWMTCategoryEntry.NAME_COLUMN_INDEX,
SWMTCategoryEntryTableViewerSorter.CRITERIA_NAME));
S60ToolsTableColumnData[] arr
= columnDataArr.toArray(
new S60ToolsTableColumnData[0]);
S60ToolsTable tbl = S60ToolsTableFactory.createCheckboxTable(parent, arr);
CheckboxTableViewer tblViewer = new CheckboxTableViewer(tbl.getTableInstance());
tbl.setHostingViewer(tblViewer);
return tblViewer;
}
/**
* Returns currently selected categories.
* @return currently selected categories
*/
private Object[] getSelectedCategoryEntries() {
return categoryViewer.getCheckedElements();
}
/**
* Stores currently selected categories combined with bitwise as a single integer.
* @param selectedCategoryEntries array of selected category entries
*/
private void updateSelectedCategories(Object[] selectedCategoryEntries) {
// Re-initializing category selection
categorySelection = SWMTCategoryConstants.CATEGORY_NONE;
for (int i = 0; i < selectedCategoryEntries.length; i++) {
SWMTCategoryEntry categoryEntry = (SWMTCategoryEntry) selectedCategoryEntries[i];
categorySelection = categorySelection | categoryEntry.getCategoryId();
}
}
/**
* Returns currently selected categories combined with bitwise as a single integer.
* @return currently selected categories combined with bitwise as a single integer.
*/
public int getSelectedCategories() {
return categorySelection;
}
}