--- old/application/org.openjdk.jmc.flightrecorder.ui/src/main/java/org/openjdk/jmc/flightrecorder/ui/pages/ThreadsPage.java 2019-10-22 09:27:17.235089486 -0400 +++ new/application/org.openjdk.jmc.flightrecorder.ui/src/main/java/org/openjdk/jmc/flightrecorder/ui/pages/ThreadsPage.java 2019-10-22 09:27:17.077087228 -0400 @@ -1,6 +1,7 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. - * + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, Red Hat Inc. All rights reserved. + * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The contents of this file are subject to the terms of either the Universal Permissive License @@ -10,17 +11,17 @@ * * Redistribution and use in source and binary forms, with or without modification, are permitted * provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of conditions * and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list of * conditions and the following disclaimer in the documentation and/or other materials provided with * the distribution. - * + * * 3. Neither the name of the copyright holder nor the names of its contributors may be used to * endorse or promote products derived from this software without specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR @@ -44,8 +45,14 @@ import org.eclipse.jface.action.IAction; import org.eclipse.jface.action.Separator; import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.wizard.WizardPage; +import org.eclipse.osgi.util.NLS; import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Item; import org.eclipse.ui.forms.widgets.FormToolkit; import org.openjdk.jmc.common.IMCThread; import org.openjdk.jmc.common.IState; @@ -69,22 +76,28 @@ import org.openjdk.jmc.flightrecorder.ui.IPageUI; import org.openjdk.jmc.flightrecorder.ui.StreamModel; import org.openjdk.jmc.flightrecorder.ui.common.AbstractDataPage; +import org.openjdk.jmc.flightrecorder.ui.common.FilterComponent; import org.openjdk.jmc.flightrecorder.ui.common.FlavorSelector.FlavorSelectorState; import org.openjdk.jmc.flightrecorder.ui.common.ImageConstants; import org.openjdk.jmc.flightrecorder.ui.common.ItemHistogram; import org.openjdk.jmc.flightrecorder.ui.common.ItemHistogram.HistogramSelection; import org.openjdk.jmc.flightrecorder.ui.common.ItemHistogram.ItemHistogramBuilder; import org.openjdk.jmc.flightrecorder.ui.common.ItemRow; +import org.openjdk.jmc.flightrecorder.ui.common.DropdownLaneFilter; import org.openjdk.jmc.flightrecorder.ui.common.ThreadGraphLanes; import org.openjdk.jmc.flightrecorder.ui.messages.internal.Messages; +import org.openjdk.jmc.flightrecorder.ui.selection.SelectionStoreActionToolkit; import org.openjdk.jmc.ui.UIPlugin; import org.openjdk.jmc.ui.charts.IXDataRenderer; import org.openjdk.jmc.ui.charts.QuantitySpanRenderer; import org.openjdk.jmc.ui.charts.RendererToolkit; import org.openjdk.jmc.ui.column.ColumnManager.SelectionState; +import org.openjdk.jmc.ui.column.ColumnMenusFactory; import org.openjdk.jmc.ui.column.TableSettings; import org.openjdk.jmc.ui.handlers.ActionToolkit; import org.openjdk.jmc.ui.handlers.MCContextMenuManager; +import org.openjdk.jmc.ui.wizards.IPerformFinishable; +import org.openjdk.jmc.ui.wizards.OnePageWizardDialog; public class ThreadsPage extends AbstractDataPage { @@ -153,10 +166,11 @@ Messages.JavaApplicationPage_COLUMN_THREAD_DURATION_DESC); } - private class ThreadsPageUi extends ChartAndTableUI { + private class ThreadsPageUi extends ChartAndPopupTableUI { private static final String THREADS_TABLE_FILTER = "threadsTableFilter"; //$NON-NLS-1$ private static final String HIDE_THREAD = "hideThread"; //$NON-NLS-1$ private static final String RESET_CHART = "resetChart"; //$NON-NLS-1$ + private static final String TABLE = "table"; //$NON-NLS-1$ private Boolean isChartMenuActionsInit; private Boolean isChartModified; private Boolean reloadThreads; @@ -165,6 +179,7 @@ private List threadRows; private MCContextMenuManager mm; private ThreadGraphLanes lanes; + private DropdownLaneFilter laneFilter; ThreadsPageUi(Composite parent, FormToolkit toolkit, IPageContainer editor, IState state) { super(pageFilter, getDataSource(), parent, toolkit, editor, state, getName(), pageFilter, getIcon(), @@ -174,19 +189,27 @@ addActionsToContextMenu(mm); // FIXME: The lanes field is initialized by initializeChartConfiguration which is called by the super constructor. This is too indirect for SpotBugs to resolve and should be simplified. lanes.updateContextMenu(mm, false); - form.getToolBarManager() .add(ActionToolkit.action(() -> lanes.openEditLanesDialog(mm, false), Messages.ThreadsPage_EDIT_LANES, FlightRecorderUI.getDefault().getMCImageDescriptor(ImageConstants.ICON_LANES_EDIT))); + form.getToolBarManager() + .add(ActionToolkit.action(() -> openViewThreadDetailsDialog(state), Messages.ThreadsPage_VIEW_THREAD_DETAILS, + FlightRecorderUI.getDefault().getMCImageDescriptor(ImageConstants.ICON_TABLE))); form.getToolBarManager().update(true); chartLegend.getControl().dispose(); + setupFilterBar(); buildChart(); - table.getManager().setSelectionState(histogramSelectionState); - tableFilterComponent.loadState(state.getChild(THREADS_TABLE_FILTER)); chart.setVisibleRange(visibleRange.getStart(), visibleRange.getEnd()); onFilterChange(tableFilter); } + private void setupFilterBar() { + laneFilter = new DropdownLaneFilter(filterBar, lanes, mm); + laneFilter.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false)); + filterBar.setChart(chart); + filterBar.setChartCanvas(chartCanvas); + } + /** * Hides a thread from the chart and rebuilds the chart */ @@ -277,12 +300,12 @@ @Override protected IXDataRenderer getChartRenderer(IItemCollection itemsInTable, HistogramSelection tableSelection) { List rows = new ArrayList<>(); - + ItemHistogram histogram = getUndisposedTable(); IItemCollection selectedItems; HistogramSelection selection; if (tableSelection.getRowCount() == 0) { selectedItems = itemsInTable; - selection = table.getAllRows(); + selection = histogram.getAllRows(); } else { selectedItems = tableSelection.getItems(); selection = tableSelection; @@ -293,6 +316,8 @@ this.threadRows = selection .getSelectedRows((object, items) -> lanes.buildThreadRenderer(object, items)) .collect(Collectors.toList()); + chartCanvas.setNumItems(this.threadRows.size()); + textCanvas.setNumItems(this.threadRows.size()); this.isChartModified = false; if (this.isChartMenuActionsInit) { setResetChartActionEnablement(false); @@ -325,13 +350,12 @@ @Override public void saveTo(IWritableState state) { super.saveTo(state); - tableFilterComponent.saveState(state.createChild(THREADS_TABLE_FILTER)); saveToLocal(); } private void saveToLocal() { flavorSelectorState = flavorSelector.getFlavorSelectorState(); - histogramSelectionState = table.getManager().getSelectionState(); + histogramSelectionState = getUndisposedTable().getManager().getSelectionState(); visibleRange = chart.getVisibleRange(); } @@ -343,8 +367,76 @@ lanes = new ThreadGraphLanes(() -> getDataSource(), () -> buildChart()); return lanes.initializeChartConfiguration(Stream.of(state.getChildren(THREAD_LANE))); } + + private TablePopup tablePopup; + public void openViewThreadDetailsDialog(IState state) { + tablePopup = new TablePopup(state); + OnePageWizardDialog.openAndHideCancelButton(tablePopup, 500, 600); + } + + private class TablePopup extends WizardPage implements IPerformFinishable { + + private IState state; + + protected TablePopup(IState state) { + super("ThreadDetailsPage"); //$NON-NLS-1$ + this.state = state; + setTitle(Messages.ThreadsPage_TABLE_POPUP_TITLE); + setDescription(Messages.ThreadsPage_TABLE_POPUP_DESCRIPTION); + } + + @Override + public void createControl(Composite parent) { + table = buildHistogram(parent, state.getChild(TABLE)); + MCContextMenuManager mm = MCContextMenuManager.create(table.getManager().getViewer().getControl()); + ColumnMenusFactory.addDefaultMenus(table.getManager(), mm); + table.getManager().getViewer().addSelectionChangedListener(e -> buildChart()); + table.getManager().getViewer() + .addSelectionChangedListener(e -> pageContainer.showSelection(table.getSelection().getItems())); + SelectionStoreActionToolkit.addSelectionStoreActions(pageContainer.getSelectionStore(), table, + NLS.bind(Messages.ChartAndTableUI_HISTOGRAM_SELECTION, getName()), mm); + tableFilterComponent = FilterComponent.createFilterComponent(table.getManager().getViewer().getControl(), + table.getManager(), tableFilter, model.getItems().apply(pageFilter), + pageContainer.getSelectionStore()::getSelections, this::onFilterChangeHelper); + mm.add(tableFilterComponent.getShowFilterAction()); + mm.add(tableFilterComponent.getShowSearchAction()); + table.getManager().setSelectionState(histogramSelectionState); + tableFilterComponent.loadState(state.getChild(THREADS_TABLE_FILTER)); + onFilterChange(tableFilter); + + if (selectionInput != null) { + table.getManager().getViewer().setSelection(new StructuredSelection(selectionInput)); + } + + Item[] columnWidgets = ((TableViewer) table.getManager().getViewer()).getTable().getColumns(); + for (Item columWidget : columnWidgets) { + columWidget.addListener(SWT.Selection, e -> columnSortChanged()); + } + + setControl(parent); + } + + private void columnSortChanged() { + if (!table.getSelection().getItems().hasItems()) { + buildChart(); + } + } + + private void onFilterChangeHelper(IItemFilter filter) { + onFilterChange(filter); + } + + @Override + public boolean performFinish() { + IItemCollection lastSelection = table.getSelection().getItems(); + table.show(lastSelection); + selectionInput = (Object[]) table.getManager().getViewer().getInput(); + return true; + } + } } + private Object[] selectionInput; private FlavorSelectorState flavorSelectorState; private SelectionState histogramSelectionState; private IItemFilter tableFilter;