package rocks.inspectit.ui.rcp.editor.table; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.IBaseLabelProvider; import org.eclipse.jface.viewers.IDoubleClickListener; import org.eclipse.jface.viewers.ISelectionProvider; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.TableViewerColumn; import org.eclipse.swt.SWT; import org.eclipse.swt.events.ControlAdapter; import org.eclipse.swt.events.ControlEvent; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; 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.Listener; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableColumn; import org.eclipse.swt.widgets.TableItem; import org.eclipse.ui.forms.widgets.FormToolkit; import rocks.inspectit.ui.rcp.editor.AbstractSubView; import rocks.inspectit.ui.rcp.editor.ISubView; import rocks.inspectit.ui.rcp.editor.preferences.IPreferenceGroup; import rocks.inspectit.ui.rcp.editor.preferences.PreferenceEventCallback.PreferenceEvent; import rocks.inspectit.ui.rcp.editor.preferences.PreferenceId; import rocks.inspectit.ui.rcp.editor.root.FormRootEditor; import rocks.inspectit.ui.rcp.editor.root.SubViewClassificationController.SubViewClassification; import rocks.inspectit.ui.rcp.editor.search.ISearchExecutor; import rocks.inspectit.ui.rcp.editor.search.criteria.SearchCriteria; import rocks.inspectit.ui.rcp.editor.search.criteria.SearchResult; import rocks.inspectit.ui.rcp.editor.search.helper.TableViewerSearchHelper; import rocks.inspectit.ui.rcp.editor.table.input.TableInputController; import rocks.inspectit.ui.rcp.editor.tooltip.ColumnAwareToolTipSupport; import rocks.inspectit.ui.rcp.editor.tooltip.IColumnToolTipProvider; import rocks.inspectit.ui.rcp.editor.viewers.CheckedDelegatingIndexLabelProvider; import rocks.inspectit.ui.rcp.editor.viewers.StyledCellIndexLabelProvider; import rocks.inspectit.ui.rcp.handlers.ShowHideColumnsHandler; import rocks.inspectit.ui.rcp.menu.ShowHideMenuManager; import rocks.inspectit.ui.rcp.util.SafeExecutor; /** * Sub-view which is used to create a table. * * @author Patrice Bouillet * */ public class TableSubView extends AbstractSubView implements ISearchExecutor { /** * The referenced input controller. */ private final TableInputController tableInputController; /** * The created table viewer. */ private TableViewer tableViewer; /** * Defines if a job is currently already executing. */ private volatile boolean jobInSchedule = false; /** * {@link TableViewerSearchHelper}. */ private TableViewerSearchHelper tableViewerSearchHelper; /** * Default constructor which needs a tree input controller to create all the content etc. * * @param tableInputController * The table input controller. */ public TableSubView(TableInputController tableInputController) { Assert.isNotNull(tableInputController); this.tableInputController = tableInputController; } /** * {@inheritDoc} */ @Override public void init() { tableInputController.setInputDefinition(getRootEditor().getInputDefinition()); } /** * {@inheritDoc} */ @Override public void createPartControl(Composite parent, FormToolkit toolkit) { // the style can not be SWT.VIRTUAL when the SWT.CHECK style is used // the check of the elements won't work because of the virtual loading int style = SWT.MULTI | SWT.FULL_SELECTION | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL; if (tableInputController.isCheckStyle()) { style |= SWT.CHECK; } else { style |= SWT.VIRTUAL; } final Table table = toolkit.createTable(parent, style); table.setHeaderVisible(true); table.setLinesVisible(true); tableViewer = new TableViewer(table); if (tableInputController.isCheckStyle()) { TableViewerColumn viewerColumn = new TableViewerColumn(tableViewer, SWT.NONE); viewerColumn.getColumn().setMoveable(false); viewerColumn.getColumn().setResizable(true); viewerColumn.getColumn().setWidth(30); viewerColumn.getColumn().setText("Selected"); } tableInputController.createColumns(tableViewer); tableViewer.setUseHashlookup(true); tableViewer.setContentProvider(tableInputController.getContentProvider()); IBaseLabelProvider labelProvider = tableInputController.getLabelProvider(); if (tableInputController.isCheckStyle() && (labelProvider instanceof StyledCellIndexLabelProvider)) { labelProvider = new CheckedDelegatingIndexLabelProvider((StyledCellIndexLabelProvider) labelProvider); } tableViewer.setLabelProvider(labelProvider); if (labelProvider instanceof IColumnToolTipProvider) { ColumnAwareToolTipSupport.enableFor(tableViewer); } tableViewer.addDoubleClickListener(new IDoubleClickListener() { @Override public void doubleClick(DoubleClickEvent event) { tableInputController.doubleClick(event); } }); tableViewer.setComparator(tableInputController.getComparator()); if (null != tableViewer.getComparator()) { TableColumn[] tableColumns = tableViewer.getTable().getColumns(); for (TableColumn tableColumn : tableColumns) { tableColumn.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { tableViewer.refresh(); } }); } } // add show hide columns support MenuManager headerMenuManager = new ShowHideMenuManager(tableViewer, tableInputController.getClass()); headerMenuManager.setRemoveAllWhenShown(false); // normal selection menu manager MenuManager selectionMenuManager = new MenuManager(); selectionMenuManager.setRemoveAllWhenShown(true); getRootEditor().getSite().registerContextMenu(FormRootEditor.ID + ".tablesubview", selectionMenuManager, tableViewer); final Menu selectionMenu = selectionMenuManager.createContextMenu(table); final Menu headerMenu = headerMenuManager.createContextMenu(table); table.addListener(SWT.MenuDetect, new Listener() { @Override public void handleEvent(Event event) { Point pt = Display.getDefault().map(null, table, new Point(event.x, event.y)); Rectangle clientArea = table.getClientArea(); boolean header = (clientArea.y <= pt.y) && (pt.y < (clientArea.y + table.getHeaderHeight())); if (header) { table.setMenu(headerMenu); } else { table.setMenu(selectionMenu); } } }); /** * IMPORTANT: Only the menu set in the setMenu() will be disposed automatically. */ table.addListener(SWT.Dispose, new Listener() { @Override public void handleEvent(Event event) { if (!headerMenu.isDisposed()) { headerMenu.dispose(); } if (!selectionMenu.isDisposed()) { selectionMenu.dispose(); } } }); Object input = tableInputController.getTableInput(); tableViewer.setInput(input); ControlAdapter columnResizeListener = new ControlAdapter() { @Override public void controlResized(ControlEvent e) { if (e.widget instanceof TableColumn) { TableColumn column = (TableColumn) e.widget; if (column.getWidth() > 0) { ShowHideColumnsHandler.registerNewColumnWidth(tableInputController.getClass(), column.getText(), column.getWidth()); } } } @Override public void controlMoved(ControlEvent e) { ShowHideColumnsHandler.setColumnOrder(tableInputController.getClass(), tableViewer.getTable().getColumnOrder()); } }; for (TableColumn column : table.getColumns()) { if (tableInputController.canAlterColumnWidth(column)) { Integer rememberedWidth = ShowHideColumnsHandler.getRememberedColumnWidth(tableInputController.getClass(), column.getText()); boolean isColumnHidden = ShowHideColumnsHandler.isColumnHidden(tableInputController.getClass(), column.getText()); if ((rememberedWidth != null) && !isColumnHidden) { column.setWidth(rememberedWidth.intValue()); column.setResizable(true); } else if (isColumnHidden) { column.setWidth(0); column.setResizable(false); } } column.addControlListener(columnResizeListener); } // update the order of columns if the order was defined for the class, and no new columns // were added int[] columnOrder = ShowHideColumnsHandler.getColumnOrder(tableInputController.getClass()); if ((null != columnOrder) && (columnOrder.length == table.getColumns().length)) { table.setColumnOrder(columnOrder); } else if (null != columnOrder) { // if the order exists, but length is not same, then update with the default order ShowHideColumnsHandler.setColumnOrder(tableInputController.getClass(), table.getColumnOrder()); } tableViewerSearchHelper = new TableViewerSearchHelper(tableViewer, tableInputController, getRootEditor().getInputDefinition().getRepositoryDefinition()); // add listener for the check box style if active if (tableInputController.isCheckStyle()) { table.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { if (e.detail == SWT.CHECK) { if (e.item instanceof TableItem) { TableItem item = (TableItem) e.item; tableInputController.objectChecked(item.getData(), item.getChecked()); } } } }); } } /** * {@inheritDoc} */ @Override public void doRefresh() { if (!jobInSchedule) { jobInSchedule = true; Job job = new Job(getDataLoadingJobName()) { @Override protected IStatus run(IProgressMonitor monitor) { try { tableInputController.doRefresh(monitor, getRootEditor()); SafeExecutor.asyncExec(new Runnable() { @Override public void run() { if (checkDisposed()) { return; } // refresh should only influence the master sub views if (tableInputController.getSubViewClassification() == SubViewClassification.MASTER) { Object input = tableInputController.getTableInput(); tableViewer.setInput(input); if (tableViewer.getTable().isVisible()) { tableViewer.refresh(); // if we use check style, set elements to the initial state if (tableInputController.isCheckStyle()) { for (TableItem tableItem : tableViewer.getTable().getItems()) { tableItem.setChecked(tableInputController.areItemsInitiallyChecked()); } } } } } }, tableViewer.getTable()); } catch (Throwable throwable) { // NOPMD throw new RuntimeException("Unknown exception occurred trying to refresh the view.", throwable); } finally { jobInSchedule = false; } return Status.OK_STATUS; } }; job.schedule(); } } /** * {@inheritDoc} */ @Override public void setDataInput(List<? extends Object> data) { if (checkDisposed()) { return; } if (tableInputController.canOpenInput(data)) { tableViewer.setInput(data); tableViewer.refresh(true); } } /** * {@inheritDoc} */ @Override public Control getControl() { return tableViewer.getControl(); } /** * {@inheritDoc} */ @Override public ISelectionProvider getSelectionProvider() { return tableViewer; } /** * {@inheritDoc} */ @Override public Set<PreferenceId> getPreferenceIds() { return tableInputController.getPreferenceIds(); } /** * {@inheritDoc} */ @Override public void preferenceEventFired(PreferenceEvent preferenceEvent) { if (checkDisposed()) { return; } if (PreferenceId.ITEMCOUNT.equals(preferenceEvent.getPreferenceId())) { Map<IPreferenceGroup, Object> preferenceMap = preferenceEvent.getPreferenceMap(); int limit = (Integer) preferenceMap.get(PreferenceId.ItemCount.COUNT_SELECTION_ID); tableInputController.setLimit(limit); this.doRefresh(); } // we are suspending the table redraw while controller handles the event to disable any // updates on the table by controller tableViewer.getTable().setRedraw(false); tableInputController.preferenceEventFired(preferenceEvent); tableViewer.getTable().setRedraw(true); switch (preferenceEvent.getPreferenceId()) { case CLEAR_BUFFER: if (tableInputController.getPreferenceIds().contains(PreferenceId.CLEAR_BUFFER)) { tableViewer.refresh(); } break; case TIME_RESOLUTION: if (tableInputController.getPreferenceIds().contains(PreferenceId.TIME_RESOLUTION)) { tableViewer.refresh(); } break; case INVOCATION_SUBVIEW_MODE: if (tableInputController.getPreferenceIds().contains(PreferenceId.INVOCATION_SUBVIEW_MODE)) { tableViewer.refresh(); } break; default: break; } } /** * Returns the table input controller. * * @return The table input controller. */ public TableInputController getTableInputController() { return tableInputController; } /** * Return the names of all columns in the table. Not visible columns names will also be * included. The order of the names will be same to the initial table column order, thus not * reflecting the current state of the table if the columns were moved. * * @return List of column names. */ public List<String> getColumnNames() { List<String> names = new ArrayList<>(); for (TableColumn column : tableViewer.getTable().getColumns()) { names.add(column.getText()); } return names; } /** * * @return The list of integers representing the column order in the table. Note that only * columns that are currently visible will be included in the list. * @see Table#getColumnOrder() */ public List<Integer> getColumnOrder() { int[] order = tableViewer.getTable().getColumnOrder(); List<Integer> orderWithoutHidden = new ArrayList<>(); for (int index : order) { if (tableViewer.getTable().getColumns()[index].getWidth() > 0) { orderWithoutHidden.add(index); } } return orderWithoutHidden; } /** * {@inheritDoc} */ @Override public ISubView getSubViewWithInputController(Class<?> inputControllerClass) { if (Objects.equals(inputControllerClass, tableInputController.getClass())) { return this; } return null; } /** * {@inheritDoc} */ @Override public SearchResult executeSearch(SearchCriteria searchCriteria) { return tableViewerSearchHelper.executeSearch(searchCriteria); } /** * {@inheritDoc} */ @Override public SearchResult next() { return tableViewerSearchHelper.next(); } /** * {@inheritDoc} */ @Override public SearchResult previous() { return tableViewerSearchHelper.previous(); } /** * {@inheritDoc} */ @Override public void clearSearch() { tableViewerSearchHelper.clearSearch(); } /** * Returns true if the table in the sub-view is disposed. False otherwise. * * @return Returns true if the table in the sub-view is disposed. False otherwise. */ private boolean checkDisposed() { return tableViewer.getTable().isDisposed(); } /** * {@inheritDoc} */ @Override public void dispose() { tableInputController.dispose(); } }