/******************************************************************************* * Copyright (c) 2012, 2017 Red Hat, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Red Hat initial API and implementation *******************************************************************************/ package org.eclipse.linuxtools.internal.oprofile.launch.configuration; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; import org.eclipse.debug.ui.AbstractLaunchConfigurationTab; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.viewers.ILabelProvider; import org.eclipse.jface.viewers.ILabelProviderListener; import org.eclipse.jface.viewers.IStructuredContentProvider; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.ListViewer; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerComparator; import org.eclipse.jface.viewers.ViewerFilter; import org.eclipse.jface.window.IShellProvider; import org.eclipse.linuxtools.internal.oprofile.core.Oprofile.OprofileProject; import org.eclipse.linuxtools.internal.oprofile.core.OprofileCorePlugin; import org.eclipse.linuxtools.internal.oprofile.core.daemon.OpEvent; import org.eclipse.linuxtools.internal.oprofile.core.daemon.OpUnitMask; import org.eclipse.linuxtools.internal.oprofile.core.daemon.OprofileDaemonEvent; import org.eclipse.linuxtools.internal.oprofile.launch.OprofileLaunchMessages; import org.eclipse.linuxtools.internal.oprofile.launch.OprofileLaunchPlugin; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.ScrolledComposite; 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.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.TabFolder; import org.eclipse.swt.widgets.TabItem; import org.eclipse.swt.widgets.Text; public abstract class AbstractEventConfigTab extends AbstractLaunchConfigurationTab { protected Button defaultEventCheck; protected OprofileCounter[] counters = null; protected CounterSubTab[] counterSubTabs; private Composite top; /** * Essentially the constructor for this tab; creates the 'default event' * checkbox and an appropriate number of counter tabs. * @param parent the parent composite */ @Override public void createControl(Composite parent) { Composite top = new Composite(parent, SWT.NONE); setControl(top); top.setLayout(new GridLayout()); this.top = top; } /** * @since 1.1 * @param top */ private void createCounterTabs(Composite top){ //tabs for each of the counters counters = getOprofileCounters(null); TabItem[] counterTabs = new TabItem[counters.length]; // create only one counter for operf/opcontrol if (counters.length > 0) { counterSubTabs = new CounterSubTab[1]; } else { counterSubTabs = new CounterSubTab[0]; } TabFolder tabFolder = new TabFolder(top, SWT.NONE); tabFolder.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); // As per Roland suggestion if we decide to list all the selected events // in a separate list viewer, then it makes sense to always show just one tab. // This approach would make operf/opcontrol event selection more similar. for (int i = 0; i < counters.length; i++) { Composite c = new Composite(tabFolder, SWT.NONE); CounterSubTab currentTab = new CounterSubTab(c, counters[i]); counterSubTabs[i] = currentTab; counterTabs[i] = new TabItem(tabFolder, SWT.NONE); counterTabs[i].setControl(c); counterTabs[i].setText(OprofileLaunchMessages.getString("tab.event.counterTab.counterText")); //$NON-NLS-1$ // just one tab for operf/opcontrol break; } getTabFolderComposite(); } /** * @since 1.1 */ private Composite getTabFolderComposite(){ // check for length and first tab being null to prevent AIOBE if(counterSubTabs.length == 0 ||counterSubTabs[0] == null){ return null; } else { Composite c = counterSubTabs[0].getTabTopContainer(); while(c != null && !(c instanceof TabFolder)){ c = c.getParent(); } return c.getParent(); } } @Override public void initializeFrom(ILaunchConfiguration config) { IProject previousProject = getOprofileProject(); IProject project = getProject(config); setOprofileProject(project); updateOprofileInfo(); String previousHost = null; if(previousProject != null){ if(previousProject.getLocationURI() != null){ previousHost = previousProject.getLocationURI().getHost(); } } String host; if (project != null) { host = project.getLocationURI().getHost(); } else { host = null; } // Create the counter tabs if host has changed or if they haven't been created yet // Check that initialization is not done for current project. // Any calculation based on project doesn't work as the very first time for local project they are both null. if(previousProject == null || previousHost != host || host == null || counters == null){ Control[] children = top.getChildren(); for (Control control : children) { control.dispose(); } OprofileCounter [] ctrs = getOprofileCounters(null); if (getOprofileTimerMode() || (ctrs.length > 0 && ctrs[0].getValidEvents() == null)) { Label timerModeLabel = new Label(top, SWT.LEFT); timerModeLabel.setText(OprofileLaunchMessages.getString("tab.event.timermode.no.options")); //$NON-NLS-1$ } else { createVerticalSpacer(top, 1); //default event checkbox defaultEventCheck = new Button(top, SWT.CHECK); defaultEventCheck.setText(OprofileLaunchMessages.getString("tab.event.defaultevent.button.text")); //$NON-NLS-1$ defaultEventCheck.setLayoutData(new GridData()); defaultEventCheck.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent se) { handleEnabledToggle(); } }); createVerticalSpacer(top, 1); createCounterTabs(top); } } if(!getOprofileTimerMode()){ if (counters == null) { OprofileCorePlugin.showErrorDialog("countersNotFound", null); //$NON-NLS-1$ return; } for (int i = 0; i < counters.length; i++) { counters[i].loadConfiguration(config); } for (CounterSubTab tab : counterSubTabs) { tab.initializeTab(config); tab.createEventsFilter(); } try{ boolean enabledState = config.getAttribute(OprofileLaunchPlugin.ATTR_USE_DEFAULT_EVENT, true); defaultEventCheck.setSelection(enabledState); setEnabledState(!enabledState); } catch (CoreException e) { e.printStackTrace(); } } } @Override public boolean isValid(ILaunchConfiguration config) { IProject project = getProject(config); setOprofileProject(project); OprofileCounter [] ctrs = getOprofileCounters(null); if (ctrs.length > 0 && ctrs[0].getValidEvents() == null) { return false; } if (getOprofileTimerMode() || counterSubTabs == null) { return true; //no options to check for validity } else { return validateEvents(config); } } /** * Validate events specified in the given configuration. * @param config * @return */ private boolean validateEvents(ILaunchConfiguration config) { int numEnabledEvents = 0; boolean valid = true; try { if (config.getAttribute(OprofileLaunchPlugin.ATTR_USE_DEFAULT_EVENT, false)) { numEnabledEvents = 1; } else { //This seems like an odd way to validate, but since most of the validation // is done with the OprofileDaemonEvent that the counter wraps, this // is the easiest way. OprofileCounter[] counters = new OprofileCounter[getNumberOfOprofileCounters()]; for (int i = 0; i < counters.length; i++) { counters[i] = getOprofileCounter(i); counters[i].loadConfiguration(config); for (CounterSubTab counterSubTab : counterSubTabs){ int nr = counterSubTab.counter.getNumber(); if(counterSubTab.enabledCheck.getSelection() && config.getAttribute(OprofileLaunchPlugin.attrNumberOfEvents(nr), 0) == 0){ valid = false; } // if target list is empty valid is false // target event list item count int count = counterSubTab.selectedEventList.getList().getItemCount(); if(count == 0) { valid = false; } } if (counters[i].getEnabled()) { ++numEnabledEvents; for (OpEvent event : counters[i].getEvents()) { if (event == null) { valid = false; break; } // First check min count int min = event.getMinCount(); if (counters[i].getCount() < min) { Object[] args = new Object[] { min }; setErrorMessage(MessageFormat .format(OprofileLaunchMessages .getString("tab.event.counterSettings.count.too-small"), //$NON-NLS-1$ args)); valid = false; break; } // Next ask oprofile if it is valid if (!checkEventSetupValidity( counters[i].getNumber(), event.getText(), event.getUnitMask().getMaskValue())) { Object[] args = new Object[] { event.getText() }; setErrorMessage(MessageFormat.format(OprofileLaunchMessages.getString("tab.event.validation.msg"), args)); //$NON-NLS-1$ valid = false; break; } } } } } } catch (CoreException e) { e.printStackTrace(); } return (numEnabledEvents > 0 && valid); } @Override public void performApply(ILaunchConfigurationWorkingCopy config) { if (getOprofileTimerMode() || counterSubTabs == null) { config.setAttribute(OprofileLaunchPlugin.ATTR_USE_DEFAULT_EVENT, true); } else { config.setAttribute(OprofileLaunchPlugin.ATTR_USE_DEFAULT_EVENT, defaultEventCheck.getSelection()); for (CounterSubTab cst : counterSubTabs) { cst.performApply(config); } } } @Override public void setDefaults(ILaunchConfigurationWorkingCopy config) { boolean useDefault = true; IProject project = getProject(config); setOprofileProject(project); counters = getOprofileCounters(config); // When instantiated, the OprofileCounter will set defaults. for (int i = 0; i < counters.length; i++) { counters[i].saveConfiguration(config); if (counters[i].getEnabled()) { useDefault = false; } } config.setAttribute(OprofileLaunchPlugin.ATTR_USE_DEFAULT_EVENT, useDefault); } @Override public String getName() { return OprofileLaunchMessages.getString("tab.event.name"); //$NON-NLS-1$ } @Override public Image getImage() { return OprofileLaunchPlugin.getImageDescriptor(OprofileLaunchPlugin.ICON_EVENT_TAB).createImage(); } /** * Handles the toggling of the default event check box. Not meant to be called * directly. */ private void handleEnabledToggle() { setEnabledState(!defaultEventCheck.getSelection()); updateLaunchConfigurationDialog(); } /** * Sets the state of the child counter tabs' widgets. * @param state true for enabled, false for disabled */ private void setEnabledState(boolean state) { for (CounterSubTab cst : counterSubTabs) { cst.setEnabledState(state); } } /* * Extracted methods to be overridden by the test suite. */ /** * Returns whether the event's unit mask is valid * @param counter counter number * @param name event name * @param maskValue unit mask value * @return true if valid config, false otherwise */ protected abstract boolean checkEventSetupValidity(int counter, String name, int maskValue); /** * * @param config * @return * @since 1.1 */ private IProject getProject(ILaunchConfiguration config){ String name = null; try { name = config.getAttribute(ICDTLaunchConfigurationConstants.ATTR_PROJECT_NAME, ""); //$NON-NLS-1$ } catch (CoreException e) { return null; } if (name.isEmpty()) { return null; } return ResourcesPlugin.getWorkspace().getRoot().getProject(name); } /** * Returns counter with corresponding to counter number. * @param i the counter number */ public abstract OprofileCounter getOprofileCounter(int i); /** * Returns counters in the given configuration. * @param config the launch configuration */ protected abstract OprofileCounter[] getOprofileCounters(ILaunchConfiguration config); /** * Returns the number of hardware counters the cpu has * @return int number of counters */ protected abstract int getNumberOfOprofileCounters(); /** * Returns whether or not oprofile is operating in timer mode. * @return true if oprofile is in timer mode, false otherwise */ protected abstract boolean getOprofileTimerMode(); /** * Returns current project to profile by Oprofile. */ protected abstract IProject getOprofileProject(); /** * Set project to profile by Oprofile. * @param project the project to profile */ protected abstract void setOprofileProject(IProject project); /** * Update generic Oprofile information. */ protected abstract void updateOprofileInfo(); /** * A sub-tab of the OprofileEventConfigTab launch configuration tab. * Essentially, it is a frontend to an OprofileCounter. This is an * inner class because it requires methods from the parent tab (such as * updateLaunchConfigurationDialog() when a widget changes state). */ protected class CounterSubTab { private Button profileKernelCheck; private Button profileUserCheck; private Label countTextLabel; private Text countText; private Label eventDescLabel; private Text eventDescText; private UnitMaskViewer unitMaskViewer; private Text eventFilterText; private OprofileCounter counter; private ScrolledComposite scrolledTop; protected Composite tabTopContainer; protected Button enabledCheck; protected ListViewer eventList; protected Button add; protected Button addAll; protected Button remove; protected Button removeAll; protected Button customizeBtn; protected ListViewer selectedEventList; protected ScrolledComposite unitmaskScrollComposite; protected Composite unitMaskSubComposite ; private static final int ADD = 1; private static final int ADD_ALL = 2; private static final int REMOVE = 3; private static final int REMOVE_ALL = 4; private static final int CUSTOMIZE = 5; private List<OpEvent> sourceList = new ArrayList<>(0); private List<OpEvent> targetList = new ArrayList<>(0); public Composite getTabTopContainer() { return tabTopContainer; } public void setTabTopContainer(Composite tabTopContainer) { this.tabTopContainer = tabTopContainer; } /** * Constructor for a subtab. Creates the layout and widgets for its content. * @param parent composite the widgets will be created in * @param counter the associated OprofileCounter object */ public CounterSubTab(Composite parent, OprofileCounter counter) { this.counter = counter; parent.setLayout(new GridLayout()); //scrollable composite on top ScrolledComposite scrolledContainer = new ScrolledComposite(parent, SWT.H_SCROLL | SWT.V_SCROLL); scrolledContainer.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); GridLayout layout = new GridLayout(); layout.marginHeight = 0; layout.marginWidth = 0; scrolledContainer.setLayout(layout); scrolledContainer.setExpandHorizontal(true); scrolledContainer.setExpandVertical(true); //composite to contain the rest of the tab Composite tabTopContainer = new Composite(scrolledContainer, SWT.NONE); scrolledContainer.setContent(tabTopContainer); layout = new GridLayout(); layout.marginHeight = 0; layout.marginWidth = 0; layout.numColumns = 2; tabTopContainer.setLayout(layout); tabTopContainer.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); //top cell Composite topCellComp = new Composite(tabTopContainer, SWT.NONE); layout = new GridLayout(); layout.marginHeight = 0; layout.marginWidth = 0; layout.numColumns = 2; topCellComp.setLayout(layout); topCellComp.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false, 2, 1)); createTopCell(topCellComp); createVerticalSpacer(tabTopContainer, 2); //left side composite group for eventList Composite eventListComp = new Composite(tabTopContainer, SWT.NONE); layout = new GridLayout(); layout.marginHeight = 0; layout.marginWidth = 0; layout.numColumns = 3; eventListComp.setLayout(layout); eventListComp.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); //layoutdata is set later createLeftCell(eventListComp); scrolledTop = scrolledContainer; this.tabTopContainer = tabTopContainer; resizeScrollContainer(); } /** * Creates the "Enabled" checkbox, and the event description text. * @param parent composite these widgets will be created in */ private void createTopCell(Composite parent) { //checkbox enabledCheck = new Button(parent, SWT.CHECK); enabledCheck.setText(OprofileLaunchMessages.getString("tab.event.counterSettings.enabled.button.text")); //$NON-NLS-1$ enabledCheck.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1)); enabledCheck.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent se) { counter.setEnabled(enabledCheck.getSelection()); internalSetEnabledState(counter.getEnabled()); updateLaunchConfigurationDialog(); } }); enabledCheck.setEnabled(false); //label for textbox eventDescLabel = new Label(parent, SWT.NONE); eventDescLabel.setText(OprofileLaunchMessages.getString("tab.event.eventDescription.label.text")); //$NON-NLS-1$ eventDescLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false)); //textbox eventDescText = new Text(parent, SWT.SINGLE | SWT.BORDER | SWT.READ_ONLY); eventDescText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); } /** * Creates the event list widget. * @param parent composite these widgets will be created in */ private void createLeftCell(Composite parent) { // Text box used to filter the event list eventFilterText = new Text(parent, SWT.BORDER | SWT.SINGLE | SWT.ICON_CANCEL | SWT.SEARCH); eventFilterText.setMessage(OprofileLaunchMessages.getString("tab.event.eventfilter.message")); //$NON-NLS-1$ GridData eventFilterLayout = new GridData(); eventFilterLayout.horizontalAlignment = SWT.FILL; eventFilterLayout.grabExcessHorizontalSpace = true; eventFilterText.setLayoutData(eventFilterLayout); eventFilterText.addModifyListener(e -> eventList.refresh(false)); // profile user binary and profile kernel createRightCell(parent); int options = SWT.V_SCROLL | SWT.H_SCROLL | SWT.BORDER; if (OprofileProject.getProfilingBinary().equals(OprofileProject.OPERF_BINARY)) { options |= SWT.MULTI; } else { options |= SWT.SINGLE; } eventList = new ListViewer(parent, options); GridData gdata = new GridData(SWT.FILL, SWT.FILL, true, true); gdata.verticalSpan = 5; eventList.getList().setLayoutData(gdata); eventList.setLabelProvider(new LabelProvider(){ @Override public String getText(Object element) { OpEvent e = (OpEvent) element; return e.getText(); } @Override public Image getImage(Object element) { return null; } @Override public boolean isLabelProperty(Object element, String property) { return false; } }); eventList.setContentProvider(new IStructuredContentProvider() { @Override public Object[] getElements(Object inputElement) { List<OpEvent> list = (List<OpEvent>)inputElement; return list.toArray(); } @Override public void dispose() { } @Override public void inputChanged(Viewer arg0, Object arg1, Object arg2) { } }); // sorter ListviewerComparator comparator = new ListviewerComparator(); eventList.setComparator(comparator); //adds the events to the list from the counter sourceList.addAll(Arrays.asList(counter.getValidEvents())); eventList.setInput(sourceList); eventList.addSelectionChangedListener(sce -> handleEventListSelectionChange()); HandleButtonClick listener = new HandleButtonClick(); add = new Button(parent, SWT.PUSH); add.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); add.setText(OprofileLaunchMessages.getString("tab.event.addevent.button.text")); //$NON-NLS-1$ add.setData(ADD); add.addListener(SWT.Selection, listener); selectedEventList = new ListViewer(parent, options); selectedEventList.getList().setLayoutData(gdata); selectedEventList.setLabelProvider(new ILabelProvider(){ @Override public String getText(Object element) { OpEvent e = (OpEvent) element; return e.getText(); } @Override public Image getImage(Object element) { return null; } @Override public void addListener(ILabelProviderListener listener) { } @Override public void dispose() { } @Override public boolean isLabelProperty(Object element, String property) { return false; } @Override public void removeListener(ILabelProviderListener listener) { } }); selectedEventList.setContentProvider(new IStructuredContentProvider() { @Override public Object[] getElements(Object inputElement) { List<OpEvent> list = (List<OpEvent>)inputElement; return list.toArray(); } @Override public void dispose() { } @Override public void inputChanged(Viewer arg0, Object arg1, Object arg2) { } }); // sorter ListviewerComparator viewerComparator = new ListviewerComparator(); selectedEventList.setComparator(viewerComparator); //adds the events to the list from the counter if(counter.getEvents().length != 0 && null != counter.getEvents()[0]) { targetList.addAll(Arrays.asList(counter.getEvents())); } selectedEventList.setInput(targetList); selectedEventList.addSelectionChangedListener(sce -> { handleListSelection(selectedEventList); eventList.getList().deselectAll(); updateLaunchConfigurationDialog(); }); addAll = new Button(parent, SWT.PUSH); addAll.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); addAll.setText(OprofileLaunchMessages.getString("tab.event.addallevent.button.text")); //$NON-NLS-1$ addAll.setData(ADD_ALL); addAll.addListener(SWT.Selection, listener); remove = new Button(parent, SWT.PUSH); remove.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); remove.setText(OprofileLaunchMessages.getString("tab.event.removeevent.button.text")); //$NON-NLS-1$ remove.setData(REMOVE); remove.addListener(SWT.Selection, listener); removeAll = new Button(parent, SWT.PUSH); removeAll.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); removeAll.setText(OprofileLaunchMessages.getString("tab.event.removeallevent.button.text")); //$NON-NLS-1$ removeAll.setData(REMOVE_ALL); removeAll.addListener(SWT.Selection, listener); customizeBtn = new Button(parent, SWT.PUSH); customizeBtn.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false)); customizeBtn.setText(OprofileLaunchMessages.getString("tab.event.customizeevent.button.text")); //$NON-NLS-1$ customizeBtn.addListener(SWT.Selection, listener); customizeBtn.setData(CUSTOMIZE); } /** * Creates the 2 profile space checkboxes, event count and unit mask widget. * @param parent composite these widgets will be created in */ private void createRightCell(Composite parent) { //profile kernel checkbox profileKernelCheck = new Button(parent, SWT.CHECK); profileKernelCheck.setText(OprofileLaunchMessages.getString("tab.event.counterSettings.profileKernel.check.text")); //$NON-NLS-1$ profileKernelCheck.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent se) { handleProfileKernelToggle(); } }); //profile user checkbox -- should this ever be disabled? profileUserCheck = new Button(parent, SWT.CHECK); profileUserCheck.setText(OprofileLaunchMessages.getString("tab.event.counterSettings.profileUser.check.text")); //$NON-NLS-1$ profileUserCheck.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent se) { handleProfileUserToggle(); } }); } /** * Creates a text filter for the events list widget */ private void createEventsFilter(){ // Event Filter ViewerFilter eventFilter = new ViewerFilter() { @Override public Object[] filter(Viewer viewer, Object parent, Object[] elements) { Object[] filteredElements = super.filter(viewer,parent,elements); handleEventListSelectionChange(); return filteredElements; } @Override public boolean select(Viewer viewer, Object parentElement, Object element) { String[] filterTerms = eventFilterText.getText().trim().toLowerCase().split(" "); //$NON-NLS-1$ String eventName = ((OpEvent)element).getText().toLowerCase(); String eventDescription = ((OpEvent)element).getTextDescription().toLowerCase(); boolean contains = true; for (String filterTerm : filterTerms) { if(contains){ contains = eventName.contains(filterTerm) || eventDescription.contains(filterTerm); } } return contains; } }; if(eventList != null){ eventList.addFilter(eventFilter); } } /** * Initializes the tab on first creation. * @param config default configuration for the counter and the associated widgets */ public void initializeTab(ILaunchConfiguration config) { //make all controls inactive, since the 'default event' checkbox // is checked by default try { defaultEventCheck.setSelection(config.getAttribute(OprofileLaunchPlugin.ATTR_USE_DEFAULT_EVENT, true)); } catch (CoreException e) { e.printStackTrace(); } setEnabledState(false); if (config != null) { counter.loadConfiguration(config); } boolean enabled = counter.getEnabled(); enabledCheck.setSelection(enabled); if (counter.getEvents().length == 0 || counter.getEvents()[0] == null) { // Default to first in list counter.setEvents(new OpEvent [] {counter.getValidEvents()[0]}); } //load default states profileKernelCheck.setSelection(counter.getProfileKernel()); profileUserCheck.setSelection(counter.getProfileUser()); eventDescText.setText(counter.getEvents()[0].getTextDescription()); // add opevent to target event list ArrayList<OpEvent> tmp = new ArrayList<>(Arrays.asList(counter.getEvents())); targetList.addAll(tmp); selectedEventList.add(tmp.toArray()); selectedEventList.refresh(); selectedEventList.setSelection(new StructuredSelection(tmp.toArray())); // remove selected opevent from source list sourceList.removeAll(tmp); eventList.remove(tmp.toArray()); eventList.refresh(); } /** * Applies the tab's current state to the launch configuration. * @param config launch config to apply to */ public void performApply(ILaunchConfigurationWorkingCopy config) { counter.saveConfiguration(config); } /** * Enables/disables the widgets in this tab. * @param state true to enable to the counter's state, false to disable all */ public void setEnabledState(boolean state) { enabledCheck.setEnabled(state); if (state) { internalSetEnabledState(counter.getEnabled()); } else { internalSetEnabledState(false); } } /** * Method split from setEnabledState to avoid code duplication. * Not meant to be called directly. * @param state true to enable all widgets, false to disable all widgets */ private void internalSetEnabledState(boolean state) { profileKernelCheck.setEnabled(state); profileUserCheck.setEnabled(state); eventDescText.setEnabled(state); eventList.getList().setEnabled(state); selectedEventList.getList().setEnabled(state); eventFilterText.setEnabled(state); add.setEnabled(state); addAll.setEnabled(state); remove.setEnabled(state); removeAll.setEnabled(state); customizeBtn.setEnabled(state); } /** * Handling method for the event list. Gets the selection from the listviewer * and updates the UnitMask and event description text box. */ private void handleEventListSelectionChange() { handleListSelection(eventList); int[] indices = eventList.getList().getSelectionIndices(); if (indices.length != 0) { customizeBtn.setEnabled(true); // unselected other list element // to keep customize button enable // for both list selection selectedEventList.getList().deselectAll(); } updateLaunchConfigurationDialog(); } /** * Generic method for handling source & target selection list * @param eventList - list to be handled * @since 3.0 */ private void handleListSelection(ListViewer eventList) { setErrorMessage(null); int [] indices = eventList.getList().getSelectionIndices(); if (indices.length != 0) { ArrayList<OpEvent> tmp = new ArrayList<> (); for (int index : indices) { OpEvent event = (OpEvent) eventList.getElementAt(index); tmp.add(event); eventDescText.setText(event.getTextDescription()); } // Check the min count to update the error message (events // can have // different minimum reset counts) int min = Integer.MIN_VALUE; for (OpEvent ev : tmp) { // We want the largest of the min values if (ev.getMinCount() > min) { min = ev.getMinCount(); } } if(counter.getEvents().length == 0 || counter.getEvents()[0] == null) { counter.setEvents(new OpEvent [] {counter.getValidEvents()[0]}); } if ((counter.getCount() < min) && (!defaultEventCheck.getSelection())) { setErrorMessage(getMinCountErrorMessage(min)); } //counter.setEvents(tmp.toArray(new OpEvent[0])); } else { eventDescText.setText(""); //$NON-NLS-1$ } } /** * Handles the toggling of the "profile user" button. */ private void handleProfileUserToggle() { counter.setProfileUser(profileUserCheck.getSelection()); updateLaunchConfigurationDialog(); } /** * Handles the toggling of the "profile kernel" button. */ private void handleProfileKernelToggle() { counter.setProfileKernel(profileKernelCheck.getSelection()); updateLaunchConfigurationDialog(); } /** * Handles text modify events in the count text widget. */ private void handleCountTextModify() { String errorMessage = null; try { // This seems counter-intuitive, but we must save the count // so that isValid knows this launch config is invalid int count = Integer.parseInt(countText.getText()); counter.setCount(count); // Check minimum count int min = Integer.MIN_VALUE; for (OpEvent event : counter.getEvents()) { // We want the largest of the min values if (event != null && event.getMinCount() > min) { min = event.getMinCount(); } } if ((count < min) && (!defaultEventCheck.getSelection())) { errorMessage = getMinCountErrorMessage(min); } } catch (NumberFormatException e) { errorMessage = OprofileLaunchMessages.getString("tab.event.counterSettings.count.invalid"); //$NON-NLS-1$ counter.setCount(OprofileDaemonEvent.COUNT_INVALID); } finally { setErrorMessage(errorMessage); updateLaunchConfigurationDialog(); } } /** * Returns a string with the minimum allowed count, suitable foruse with setErrorMessage(). * @param min minimum count * @return a String containing the error message */ private String getMinCountErrorMessage(int min) { String msg = OprofileLaunchMessages.getString("tab.event.counterSettings.count.too-small"); //$NON-NLS-1$ Object[] args = new Object[] { Integer.valueOf(min) }; return MessageFormat.format(msg, args); } /** * Changes parameters for the top scrolled composite which makes the scroll bars * appear when content overflows the visible area. Called by the UnitMaskViewer * whenever a new set of unit mask buttons are created, since the number of them is * variable and there is no guarantee as to the default size of the launch configuration * dialog in general. */ private void resizeScrollContainer() { scrolledTop.setMinSize(tabTopContainer.computeSize(SWT.DEFAULT, SWT.DEFAULT)); } /** * This class displays event unit masks via check boxes and appropriate labels. */ protected class UnitMaskViewer { private Label unitMaskLabel; private Composite top; private Composite maskListComp; private Button[] unitMaskButtons; /** * Constructor, creates the widget. * @param parent composite the widget will be created in */ public UnitMaskViewer(Composite parent) { //"Unit Mask:" label unitMaskLabel = new Label(parent, SWT.NONE); unitMaskLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false)); unitMaskLabel.setText(OprofileLaunchMessages.getString("unitmaskViewer.label.text")); //$NON-NLS-1$ unitMaskLabel.setVisible(true); //composite to contain the button widgets Composite top = new Composite(parent, SWT.NONE); top.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); GridLayout layout = new GridLayout(); layout.marginHeight = 0; layout.marginWidth = 0; top.setLayout(layout); this.top = top; maskListComp = null; unitMaskButtons = null; } /** * Handles button toggles; updates the counter's unit mask to the appropriate value. * @param maskButton the button object * @param index the button's mask index (used in OpUnitMask for a proper mask value) */ private void handleToggle(Button maskButton, int index) { OpUnitMask mask = counter.getUnitMask(); if (mask != null) { if (maskButton.getSelection()) { mask.setMaskFromIndex(index); } else { mask.unSetMaskFromIndex(index); } } //update the parent tab updateLaunchConfigurationDialog(); } /** * Disposes of the old unit mask check list and creates a new one with * the appropriate default value. * @param oe the event */ public void displayEvent(OpEvent oe) { if (maskListComp != null) { maskListComp.dispose(); } if(oe == null){ return; } OpUnitMask mask = oe.getUnitMask(); int totalMasks = mask.getNumMasks(); Composite newMaskComp = new Composite(top, SWT.NONE); newMaskComp.setLayout(new GridLayout()); newMaskComp.setLayoutData(new GridData(SWT.LEFT, SWT.FILL, false, true)); maskListComp = newMaskComp; //creates these buttons with the default masks mask.setDefaultMaskValue(); ArrayList<Button> maskButtons = new ArrayList<>(); for (int i = 0; i < totalMasks; i++) { Button maskButton; if (mask.getType() == OpUnitMask.INVALID) { //big problem, most likely parsing went awry or opxml output mangled OprofileCorePlugin.showErrorDialog("opxmlParse", null); //$NON-NLS-1$ return; } else if (mask.getType() == OpUnitMask.MANDATORY) { maskButton = new Button(newMaskComp, SWT.RADIO); maskButton.setEnabled(false); maskButton.setText(mask.getText(i)); maskButton.setSelection(true); } else { int buttonType; final int maskButtonIndex = i; boolean selected = mask.isMaskSetFromIndex(maskButtonIndex); if (mask.getType() == OpUnitMask.EXCLUSIVE) { buttonType = SWT.RADIO; } else { //mask type is OpUnitMask.BITMASK buttonType = SWT.CHECK; } maskButton = new Button(newMaskComp, buttonType); maskButton.setEnabled(true); maskButton.setText(mask.getText(i)); maskButton.setSelection(selected); maskButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent se) { handleToggle((Button)se.getSource(), maskButtonIndex); } }); maskButtons.add(maskButton); } } unitMaskButtons = new Button[maskButtons.size()]; maskButtons.toArray(unitMaskButtons); resizeUnitMaskContainer(); } /** * Enables and disables the viewer for UI input * @param enabled whether this viewer should be enabled */ public void setEnabled(boolean enabled) { if (unitMaskButtons != null) { for (Button b : unitMaskButtons) { if (!b.isDisposed()) { b.setEnabled(enabled); } } } } } /** * Dialog box for unit mask field modification * @since 3.0 * */ protected class UnitMaskDialog extends Dialog { private OpEvent event; public UnitMaskDialog(IShellProvider parentShell) { super(parentShell); } public UnitMaskDialog(Shell parentShell,OpEvent event) { super(parentShell); this.event = event; } @Override protected Control createDialogArea(Composite parent) { Composite child = (Composite)super.createDialogArea(parent); ScrolledComposite scrolledContainer = new ScrolledComposite(child, SWT.H_SCROLL|SWT.V_SCROLL); scrolledContainer.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); GridLayout layout = new GridLayout(); layout.marginHeight = 0; layout.marginWidth = 0; scrolledContainer.setLayout(layout); scrolledContainer.setExpandHorizontal(true); scrolledContainer.setExpandVertical(true); Composite unitMaskSubComposite = new Composite(scrolledContainer, SWT.None); unitMaskSubComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); layout = new GridLayout(); layout.numColumns=2; unitMaskSubComposite.setLayout(layout); createUnitMaskComponents(unitMaskSubComposite); scrolledContainer.setContent(unitMaskSubComposite); CounterSubTab.this.unitmaskScrollComposite = scrolledContainer; CounterSubTab.this.unitMaskSubComposite = unitMaskSubComposite; unitMaskViewer.displayEvent(event); return child; } @Override protected boolean isResizable() { return true; } @Override protected void configureShell(Shell newShell) { super.configureShell(newShell); newShell.setText(event.getText()); newShell.setSize(400, 400); } } private void createUnitMaskComponents(Composite parent) { //event count label/text countTextLabel = new Label(parent, SWT.NONE); countTextLabel.setText(OprofileLaunchMessages.getString("tab.event.counterSettings.count.label.text")); //$NON-NLS-1$ countText = new Text(parent, SWT.SINGLE | SWT.BORDER); countText.setText(Integer.toString(counter.getCount())); countText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); countText.addModifyListener(me -> handleCountTextModify()); //unit mask widget Composite unitMaskComp = new Composite(parent, SWT.NONE); GridLayout layout = new GridLayout(); layout.marginHeight = 0; layout.marginWidth = 0; unitMaskComp.setLayout(layout); unitMaskComp.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1)); unitMaskViewer = new UnitMaskViewer(unitMaskComp); } private void resizeUnitMaskContainer() { unitmaskScrollComposite.setMinSize(unitMaskSubComposite.computeSize(SWT.DEFAULT, SWT.DEFAULT)); } /** * Events selection/removal button listener * @since 3.0 * */ protected class HandleButtonClick implements Listener { @Override public void handleEvent(Event event) { int btn_value = (Integer) event.widget.getData(); switch (btn_value) { case ADD: addButtonClicked(); updateLaunchConfigurationDialog(); break; case ADD_ALL: addAllButtonClicked(); updateLaunchConfigurationDialog(); break; case REMOVE: removeButtonClicked(); updateLaunchConfigurationDialog(); break; case REMOVE_ALL: removeAllButtonClicked(); updateLaunchConfigurationDialog(); break; case CUSTOMIZE: customizeButtonClicked(); break; default: break; } } private void addButtonClicked() { int[] indices = eventList.getList().getSelectionIndices(); if (indices.length != 0) { ArrayList<OpEvent> tmp = new ArrayList<>(); for (int index : indices) { OpEvent event = (OpEvent) eventList.getElementAt(index); tmp.add(event); } // add to target list targetList.addAll(tmp); selectedEventList.add(tmp.toArray()); sourceList.removeAll(tmp); eventList.remove(tmp.toArray()); int count = selectedEventList.getList().getItemCount(); tmp = new ArrayList<>(); for (int i = 0; i < count; i++) { OpEvent event = (OpEvent) selectedEventList .getElementAt(i); tmp.add(event); } if (!tmp.isEmpty()) counter.setEvents(tmp.toArray(new OpEvent[0])); eventList.refresh(); selectedEventList.refresh(); } } private void addAllButtonClicked() { int count = eventList.getList().getItemCount(); ArrayList<OpEvent> tmp = new ArrayList<>(); for (int i = 0; i < count; i++) { OpEvent event = (OpEvent) eventList.getElementAt(i); tmp.add(event); } targetList.addAll(tmp); selectedEventList.add(tmp.toArray()); eventList.remove(tmp.toArray()); sourceList.removeAll(tmp); count = selectedEventList.getList().getItemCount(); tmp = new ArrayList<>(); for (int i = 0; i < count; i++) { OpEvent event = (OpEvent) selectedEventList.getElementAt(i); tmp.add(event); } if (!tmp.isEmpty()) counter.setEvents(tmp.toArray(new OpEvent[0])); eventList.refresh(); selectedEventList.refresh(); } private void removeButtonClicked() { int[] indices = selectedEventList.getList() .getSelectionIndices(); if (indices.length != 0) { ArrayList<OpEvent> tmp = new ArrayList<>(); for (int index : indices) { OpEvent event = (OpEvent) selectedEventList .getElementAt(index); tmp.add(event); } // add to target list sourceList.addAll(tmp); eventList.add(tmp.toArray()); targetList.removeAll(tmp); selectedEventList.remove(tmp.toArray()); int count = selectedEventList.getList().getItemCount(); tmp = new ArrayList<>(); for (int i = 0; i < count; i++) { OpEvent event = (OpEvent) selectedEventList .getElementAt(i); tmp.add(event); } if (!tmp.isEmpty()) counter.setEvents(tmp.toArray(new OpEvent[0])); else // add first valid element to counter due to NPE counter.setEvents(new OpEvent[]{counter.getValidEvents()[0]}); eventList.refresh(); selectedEventList.refresh(); } } private void removeAllButtonClicked() { int count = selectedEventList.getList().getItemCount(); ArrayList<OpEvent> tmp = new ArrayList<>(); for (int i = 0; i < count; i++) { OpEvent event = (OpEvent) selectedEventList.getElementAt(i); tmp.add(event); } if (!tmp.isEmpty()) { sourceList.addAll(tmp); eventList.add(tmp.toArray()); selectedEventList.remove(tmp.toArray()); targetList.removeAll(tmp); } counter.setEvents(new OpEvent[] { counter.getValidEvents()[0] }); eventList.refresh(); selectedEventList.refresh(); } private void customizeButtonClicked() { UnitMaskDialog d = null; if(eventList.getList().getSelectionIndex() != -1) { d = new UnitMaskDialog(Display.getCurrent() .getActiveShell(), (OpEvent) eventList.getElementAt(eventList.getList() .getSelectionIndex())); } else if(selectedEventList.getList().getSelectionIndex() != -1) { d = new UnitMaskDialog(Display.getCurrent() .getActiveShell(), (OpEvent) selectedEventList.getElementAt(selectedEventList.getList() .getSelectionIndex())); } if(d != null) d.open(); } } } /** * * Event sorting for selected as well as all available events * @since 3.0 */ protected class ListviewerComparator extends ViewerComparator { @Override public int compare(Viewer viewer, Object e1, Object e2) { OpEvent op1 = (OpEvent) e1; OpEvent op2 = (OpEvent) e2; String op1txt = op1.getText(); String op2txt = op2.getText(); if(op1txt !=null && op2txt !=null && op1txt.trim().length() !=0 && op2txt.trim().length() !=0) return getComparator().compare(op1txt, op2txt); return super.compare(viewer, e1, e2); } } }