package org.simantics.district.selection.ui.parts; import java.awt.Color; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import javax.annotation.PostConstruct; import javax.inject.Inject; import org.eclipse.e4.core.services.events.IEventBroker; import org.eclipse.e4.ui.di.Focus; import org.eclipse.e4.ui.model.application.MApplication; import org.eclipse.e4.ui.model.application.ui.basic.MPart; import org.eclipse.e4.ui.model.application.ui.menu.MHandledToolItem; import org.eclipse.e4.ui.model.application.ui.menu.MMenuContribution; import org.eclipse.e4.ui.model.application.ui.menu.MMenuFactory; import org.eclipse.e4.ui.model.application.ui.menu.MPopupMenu; import org.eclipse.e4.ui.model.application.ui.menu.MToolBar; import org.eclipse.e4.ui.model.application.ui.menu.MToolBarElement; import org.eclipse.e4.ui.services.EMenuService; import org.eclipse.e4.ui.workbench.modeling.ESelectionService; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.simantics.Simantics; import org.simantics.browsing.ui.common.AdaptableHintContext; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.exception.DatabaseException; import org.simantics.db.layer0.SelectionHints; import org.simantics.db.layer0.request.ActiveModels; import org.simantics.db.request.Read; import org.simantics.district.network.ui.DistrictNetworkUIUtil; import org.simantics.district.network.ui.participants.DistrictFinderVisualisationParticipant; import org.simantics.district.selection.ElementSelector; import org.simantics.district.selection.ElementSelector.DiagramGenerator; import org.simantics.district.selection.ElementSelector.ExplicitGenerator; import org.simantics.district.selection.ElementSelector.PropertySelector; import org.simantics.district.selection.ElementSelector.SelectionResult; import org.simantics.district.selection.ui.ElementSelectionTools; import org.simantics.district.selection.ui.ElementSelectorTableUI; import org.simantics.scl.runtime.tuple.Tuple2; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ElementSelectionView { private static final String CONTEXT_MENU_ID = "org.simantics.district.selection.ui.contextMenu"; private static final String POPUP_ID = "org.simantics.district.selection.ui.selectiontable.popup"; private static final String CREATE_NEW_ID = "org.simantics.district.selection.ui.command.createNewSelection"; private static final String CREATE_NEW_LABEL = "Create New Element Selection Query"; private static final String CREATE_NEW_ICON = "platform:/plugin/com.famfamfam.silk/icons/add.png"; private static final String EDIT_ID = "org.simantics.district.selection.ui.command.editElementSelector"; private static final String EDIT_LABEL = "Edit Element Selection Query"; private static final String EDIT_ICON = "platform:/plugin/com.famfamfam.silk/icons/pencil.png"; private static final String DELETE_ID = "org.simantics.district.selection.ui.command.deleteElementSelector"; private static final String DELETE_LABEL = "Delete Element Selection Query"; private static final String DELETE_ICON = "platform:/plugin/com.famfamfam.silk/icons/cross.png"; /** * Name of an event that is posted to IEventBroker when a selection is made. The data value is * an instance of Collection<Resource>. */ public static final String SELECTION_EVENT_ID = "org/simantics/district/selection/elementQuerySelection"; private static final Logger LOGGER = LoggerFactory.getLogger(ElementSelectionView.class); private ElementSelectorTableUI table; @Inject private ESelectionService selectionService; @Inject private IEventBroker eventBroker; @Inject public void init(MPart part, MApplication app) { // Command is contributed via fragment MHandledToolItem createItem = MMenuFactory.INSTANCE.createHandledToolItem(); createItem.setCommand(app.getCommand(CREATE_NEW_ID)); createItem.setLabel(CREATE_NEW_LABEL); createItem.setIconURI(CREATE_NEW_ICON); MHandledToolItem editItem = MMenuFactory.INSTANCE.createHandledToolItem(); editItem.setCommand(app.getCommand(EDIT_ID)); editItem.setLabel(EDIT_LABEL); editItem.setIconURI(EDIT_ICON); MHandledToolItem deleteItem = MMenuFactory.INSTANCE.createHandledToolItem(); deleteItem.setCommand(app.getCommand(DELETE_ID)); deleteItem.setLabel(DELETE_LABEL); deleteItem.setIconURI(DELETE_ICON); MToolBar toolBar = MMenuFactory.INSTANCE.createToolBar(); toolBar.setToBeRendered(true); List children = toolBar.getChildren(); children.add(createItem); children.add(editItem); children.add(deleteItem); part.setToolbar(toolBar); MPopupMenu popupMenu = MMenuFactory.INSTANCE.createPopupMenu(); popupMenu.setElementId(POPUP_ID); List menuContributions = app.getMenuContributions(); for (MMenuContribution menuContribution : menuContributions) { if (CONTEXT_MENU_ID.equals(menuContribution.getParentId())) { popupMenu.getChildren().addAll(menuContribution.getChildren()); } } part.getMenus().add(popupMenu); } @PostConstruct public void createPartControl(Composite parent, EMenuService menuService) { table = new ElementSelectorTableUI(parent, SWT.BORDER, this); if (!(menuService.registerContextMenu(this.table.getTable(), POPUP_ID))) LOGGER.warn("Could not register context menu {}", POPUP_ID); } @Focus public void setFocus() { table.setFocus(); } public ElementSelector getSelectedItem() { return table.getSelectedItem(); } private Map resultCache = new HashMap<>(); public void performSelection(Display display, ElementSelector query) { try { Collection models = Simantics.getSession().syncRequest(new ActiveModels(Simantics.getProjectResource())); final Resource model = models.isEmpty() ? null : models.iterator().next(); Tuple2 selectionResult = resultCache.get(query); if (selectionResult != null) { // clear existing result and visualisation clearResultVisualisation(query); } SelectionResult result = performQuery(query, model); resultCache.put(query, new Tuple2(result, model)); if (result.tailCount != result.tailSize) { showArbitraryResultWarning(query, result); } if (query.getGenerator() instanceof DiagramGenerator || query.getGenerator() instanceof ExplicitGenerator) { openDiagramWithSelection(display, result); } StructuredSelection selection = makeSelection(model, result, query.getColor()); selectionService.setPostSelection(selection); sendSelectionEvent(selection); } catch (DatabaseException e) { LOGGER.error("Element selection query failed", e); } } public void clearResultVisualisation(ElementSelector query) { Tuple2 selectionResult = resultCache.get(query); if (selectionResult != null) { SelectionResult result = (SelectionResult) selectionResult.c0; Resource model = (Resource) selectionResult.c1; eventBroker.send(SELECTION_EVENT_ID, makeSelection(model, result, null)); } } private StructuredSelection makeSelection(final Resource model, SelectionResult result, float[] fs) { return new StructuredSelection(result.elements.stream() .map(p0 -> { AdaptableHintContext selectionElement = new ElementSelectionTools.SelectionElement(SelectionHints.STD_KEYS); selectionElement.setHint(SelectionHints.KEY_MAIN, p0); selectionElement.setHint(SelectionHints.KEY_MODEL, model); if (fs != null) { selectionElement.setHint(DistrictFinderVisualisationParticipant.COLOR_KEY, new Color(fs[2], fs[1], fs[0], fs[3])); } return selectionElement; }) .toArray()); } private void openDiagramWithSelection(Display display, SelectionResult result) throws DatabaseException { DistrictNetworkUIUtil.openDNDiagramWithSelection(display, new ArrayList<>(result.elements)); } private void sendSelectionEvent(Object selection) { eventBroker.send(SELECTION_EVENT_ID, selection); } private void showArbitraryResultWarning(ElementSelector query, SelectionResult result) { String name = query.getSelector() != null && query.getSelector() instanceof PropertySelector ? ((PropertySelector)query.getSelector()).propertyName : null; String msg = "Last " + result.tailCount + " of the " + result.elements.size() + " selected elements are an arbitraty subset of " + result.tailSize + " elements with equal values" + (name != null ? " for " + name : ""); MessageDialog.openInformation(Display.getDefault().getActiveShell(), "Note", msg); } private SelectionResult performQuery(ElementSelector query, final Resource model) throws DatabaseException { SelectionResult result = Simantics.getSession().syncRequest(new Read() { @Override public SelectionResult perform(ReadGraph graph) throws DatabaseException { if (model == null) { LOGGER.warn("No active model"); return new SelectionResult(Collections.emptyList(), 0, 0); } return query.selectElementsFrom(graph, model); } }); return result; } public void dispose() { for (Entry entry : resultCache.entrySet()) { ElementSelector query = entry.getKey(); clearResultVisualisation(query); } } }