/*******************************************************************************
* Copyright (c) 2006, 2016 IBM Corporation.
* 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:
* IBM Corporation - Jeff Briggs, Henry Hughes, Ryan Morse
* Red Hat Inc. - ongoing maintenance
*******************************************************************************/
package org.eclipse.linuxtools.systemtap.graphing.ui.datadisplay;
import java.text.MessageFormat;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.window.Window;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.linuxtools.internal.systemtap.graphing.ui.GraphingUIPlugin;
import org.eclipse.linuxtools.internal.systemtap.graphing.ui.Localization;
import org.eclipse.linuxtools.internal.systemtap.graphing.ui.preferences.GraphingPreferenceConstants;
import org.eclipse.linuxtools.internal.systemtap.graphing.ui.wizards.filter.AvailableFilterTypes;
import org.eclipse.linuxtools.internal.systemtap.graphing.ui.wizards.filter.SelectFilterWizard;
import org.eclipse.linuxtools.systemtap.graphing.core.datasets.IFilteredDataSet;
import org.eclipse.linuxtools.systemtap.graphing.core.filters.IDataSetFilter;
import org.eclipse.linuxtools.systemtap.structures.IFormattingStyles;
import org.eclipse.linuxtools.systemtap.structures.StringFormatter;
import org.eclipse.linuxtools.systemtap.structures.listeners.IUpdateListener;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MenuAdapter;
import org.eclipse.swt.events.MenuEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
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.IWorkbench;
import org.eclipse.ui.PlatformUI;
public class DataGrid implements IUpdateListener {
public static final int NONE = 0;
public static final int FULL_UPDATE = 1;
private IFilteredDataSet filteredDataSet;
private IFormattingStyles columnFormat[];
private Table table;
private Point clickLocation;
private IPreferenceStore prefs;
private Menu filterMenu;
private MenuItem autoResizeMenuItem;
private MenuItem jumpToEntryMenuItem;
private MenuItem removeFiltersMenuItem;
private MenuItem formatMenuItem;
private IPropertyChangeListener propertyChangeListener;
/**
* @param composite The {@link Composite} in which the grid will reside.
* @param set The data set this grid is associated with.
* @since 3.0 set must be a IFilteredDataSet.
*/
public DataGrid(Composite composite, IFilteredDataSet set) {
prefs = GraphingUIPlugin.getDefault().getPreferenceStore();
filteredDataSet = set;
clickLocation = new Point(-1, -1);
createPartControl(composite);
propertyChangeListener = event -> {
if (event.getProperty().equals(GraphingPreferenceConstants.P_MAX_DATA_ITEMS)) {
handleUpdateEvent();
}
};
prefs.addPropertyChangeListener(propertyChangeListener);
}
public Control getControl() { return table; }
private void createPartControl(Composite parent) {
table = new Table(parent, SWT.SINGLE | SWT.FULL_SELECTION);
table.setHeaderVisible(true);
table.setLinesVisible(true);
table.getVerticalBar().setVisible(true);
String[] names = filteredDataSet.getTitles();
TableColumn column = new TableColumn(table, SWT.LEFT);
column.setText(Localization.getString("DataGrid.Row")); //$NON-NLS-1$
column.pack();
column.setMoveable(false);
column.setResizable(false);
columnFormat = new IFormattingStyles[names.length];
for (int i = 0; i < names.length; i++) {
column = new TableColumn(table, SWT.LEFT);
column.setText(names[i]);
column.pack();
column.setMoveable(true);
columnFormat[i] = new StringFormatter();
}
table.setMenu(this.initMenus());
table.addListener(SWT.MouseDown, event -> {
clickLocation.x = event.x;
clickLocation.y = event.y;
});
handleUpdateEvent();
}
private Menu initMenus() {
Menu menu = new Menu(table.getShell(), SWT.POP_UP);
Menu formatMenu = new Menu(menu);
formatMenuItem = new MenuItem(menu, SWT.CASCADE);
formatMenuItem.setText(Localization.getString("DataGrid.FormatAs")); //$NON-NLS-1$
formatMenuItem.setMenu(formatMenu);
filterMenu = new Menu(menu);
MenuItem item = new MenuItem(menu, SWT.CASCADE);
item.setText(Localization.getString("DataGrid.AddFilter")); //$NON-NLS-1$
item.addSelectionListener(new AddFilterSelection());
removeFiltersMenuItem = new MenuItem(menu, SWT.CASCADE);
removeFiltersMenuItem.setText(Localization.getString("DataGrid.RemoveFilter")); //$NON-NLS-1$
removeFiltersMenuItem.setMenu(filterMenu);
IDataSetFilter[] filters = filteredDataSet.getFilters();
if (filters != null && filters.length > 0) {
for (int i = 0; i < filters.length; i++) {
item = new MenuItem(filterMenu, SWT.CASCADE);
item.setText(AvailableFilterTypes.getFilterName(filters[i].getID()));
item.setData(filters[i]);
item.addSelectionListener(new RemoveFilterSelection());
}
} else {
removeFiltersMenuItem.setEnabled(false);
}
autoResizeMenuItem = new MenuItem(menu, SWT.CHECK);
autoResizeMenuItem.setText(Localization.getString("DataGrid.AutoResize")); //$NON-NLS-1$
autoResizeMenuItem.setSelection(prefs.getBoolean(GraphingPreferenceConstants.P_AUTO_RESIZE));
jumpToEntryMenuItem = new MenuItem(menu, SWT.CHECK);
jumpToEntryMenuItem.setText(Localization.getString("DataGrid.JumpToEntry")); //$NON-NLS-1$
jumpToEntryMenuItem.setSelection(prefs.getBoolean(GraphingPreferenceConstants.P_JUMP_NEW_TABLE_ENTRY));
for (int i = 0; i < IFormattingStyles.FORMAT_TITLES.length; i++) {
item = new MenuItem(formatMenu, SWT.RADIO);
item.setText(IFormattingStyles.FORMAT_TITLES[i]);
item.addSelectionListener(new MenuFormatSelection());
}
formatMenuItem.setEnabled(filteredDataSet.getRowCount() > 0);
formatMenu.addMenuListener(new FormatMenuListener());
return menu;
}
private int getSelectedColumn() {
TableColumn[] cols = table.getColumns();
int location = 0;
for (int i = 0; i < cols.length; i++) {
if (clickLocation.x > location && clickLocation.x < (location += cols[i].getWidth())) {
return i;
}
}
return cols.length-1;
}
private class AddFilterSelection extends SelectionAdapter {
@Override
public void widgetSelected(SelectionEvent e) {
SelectFilterWizard wizard = new SelectFilterWizard(filteredDataSet.getTitles());
IWorkbench workbench = PlatformUI.getWorkbench();
wizard.init(workbench, null);
WizardDialog dialog = new WizardDialog(workbench.getActiveWorkbenchWindow().getShell(), wizard);
dialog.create();
int result = dialog.open();
if (result != Window.CANCEL) {
IDataSetFilter filter = wizard.getFilter();
removeFiltersMenuItem.setEnabled(true);
filteredDataSet.addFilter(filter);
table.removeAll();
handleUpdateEvent();
MenuItem item = new MenuItem(filterMenu, SWT.CASCADE);
item.setText(MessageFormat.format(Localization.getString("DataGrid.FilterLabel"), //$NON-NLS-1$
AvailableFilterTypes.getFilterName(filter.getID()), filteredDataSet.getTitles()[filter.getColumn()], filter.getInfo()));
item.setData(filter);
item.addSelectionListener(new RemoveFilterSelection());
}
wizard.dispose();
}
}
private class RemoveFilterSelection implements SelectionListener {
@Override
public void widgetSelected(SelectionEvent e) {
IDataSetFilter idsf = (IDataSetFilter)((MenuItem)e.widget).getData();
e.widget.dispose();
if (filterMenu.getItemCount() == 0) {
removeFiltersMenuItem.setEnabled(false);
}
if (filteredDataSet.removeFilter(idsf)) {
table.removeAll();
handleUpdateEvent();
}
}
@Override
public void widgetDefaultSelected(SelectionEvent e) {}
}
private class FormatMenuListener extends MenuAdapter {
@Override
public void menuShown(MenuEvent e) {
MenuItem[] items = ((Menu)e.widget).getItems();
boolean doubleValid = false, longValid = false;
String itemText;
int selectedCol = Math.max(1, getSelectedColumn());
for (int i = 0; i < items.length; i++) {
items[i].setSelection(false);
}
items[columnFormat[selectedCol-1].getFormat()].setSelection(true);
items[IFormattingStyles.UNFORMATED].setEnabled(true);
items[IFormattingStyles.STRING].setEnabled(true);
itemText = filteredDataSet.getRow(0)[selectedCol-1].toString();
try {
Double.parseDouble(itemText);
doubleValid = true;
} catch (NumberFormatException nfe) {}
try {
Long.parseLong(itemText);
longValid = true;
} catch (NumberFormatException nfe) {}
items[IFormattingStyles.DOUBLE].setEnabled(doubleValid);
items[IFormattingStyles.HEX].setEnabled(longValid);
items[IFormattingStyles.OCTAL].setEnabled(longValid);
items[IFormattingStyles.BINARY].setEnabled(longValid);
items[IFormattingStyles.DATE].setEnabled(longValid);
}
}
private class MenuFormatSelection extends SelectionAdapter {
@Override
public void widgetSelected(SelectionEvent e) {
int format = IFormattingStyles.UNFORMATED;
int column = Math.max(1, getSelectedColumn());
int i;
for (i = 0; i < IFormattingStyles.FORMAT_TITLES.length; i++) {
if (IFormattingStyles.FORMAT_TITLES[i].equals(((MenuItem)e.getSource()).getText())) {
format = i;
}
}
Object[] data = filteredDataSet.getColumn(column-1);
columnFormat[column-1].setFormat(format);
for (i = 0; i < table.getItemCount(); i++) {
table.getItem(i).setText(column, columnFormat[column-1].format(data[i].toString()));
}
table.redraw();
}
}
@Override
public void handleUpdateEvent() {
if (table.isDisposed()) {
return;
}
table.getDisplay().asyncExec(() -> {
if (table.isDisposed()) {
return;
}
TableItem item;
int startLocation;
int endLocation = filteredDataSet.getRowCount();
int maxItems = prefs.getInt(GraphingPreferenceConstants.P_MAX_DATA_ITEMS);
int oldSelection = table.getSelectionIndex();
//Remove old items to refresh table, and only read in as many items as will fit.
//Note that a full refresh is necessary in order for filtered data to appear correctly.
table.removeAll();
startLocation = Math.max(endLocation-maxItems, 0);
//Add all the new items to the table
Object[] os;
for (int j, i1 = startLocation; i1 < endLocation; i1++) {
item = new TableItem(table, SWT.NONE);
os = filteredDataSet.getRow(i1);
//Add 1 to the index/row num since graphs start counting rows at 1, not 0.
item.setText(0, Integer.toString(i1 + 1));
for (j = 0; j < os.length; j++) {
//Ignore null items
if (os[j] != null) {
item.setText(j+1, columnFormat[j].format(os[j].toString()));
}
}
}
//Re-select the old table selection, if there was one
if (oldSelection != -1) {
table.select(oldSelection);
}
//Resize the columns
TableColumn col = table.getColumn(0);
col.pack();
if (autoResizeMenuItem.getSelection()) {
TableColumn[] cols = table.getColumns();
for (int i2 = 1; i2 < cols.length; i2++) {
cols[i2].pack();
}
}
//Use if we want to set focus to newly added item.
//Run async so the table can be fully constructed before jumping to an entry.
if (jumpToEntryMenuItem.getSelection() && table.getItemCount() > 0) {
table.getDisplay().asyncExec(() -> table.showItem(table.getItem(table.getItemCount()-1)));
}
formatMenuItem.setEnabled(table.getItemCount() > 0);
});
}
public void dispose() {
filteredDataSet = null;
table.dispose();
table = null;
clickLocation = null;
columnFormat = null;
prefs.removePropertyChangeListener(propertyChangeListener);
propertyChangeListener = null;
}
}