/* * Copyright (C) 2007-2012 Argeo GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.argeo.slc.client.ui.dist.editors; import java.net.URL; import java.util.ArrayList; import java.util.List; import javax.jcr.Node; import javax.jcr.NodeIterator; import javax.jcr.PropertyType; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.query.QueryManager; import javax.jcr.query.QueryResult; import javax.jcr.query.qom.Constraint; import javax.jcr.query.qom.DynamicOperand; import javax.jcr.query.qom.QueryObjectModel; import javax.jcr.query.qom.QueryObjectModelFactory; import javax.jcr.query.qom.Selector; import javax.jcr.query.qom.StaticOperand; import org.argeo.ArgeoException; import org.argeo.eclipse.ui.EclipseUiUtils; import org.argeo.eclipse.ui.utils.CommandUtils; import org.argeo.jcr.JcrUtils; import org.argeo.slc.SlcException; import org.argeo.slc.client.ui.dist.DistConstants; import org.argeo.slc.client.ui.dist.DistImages; import org.argeo.slc.client.ui.dist.DistPlugin; import org.argeo.slc.client.ui.dist.commands.DeleteArtifacts; import org.argeo.slc.client.ui.dist.utils.AbstractHyperlinkListener; import org.argeo.slc.client.ui.dist.utils.NodeViewerComparator; import org.argeo.slc.jcr.SlcNames; import org.argeo.slc.jcr.SlcTypes; import org.argeo.slc.repo.RepoConstants; import org.argeo.slc.repo.RepoUtils; import org.eclipse.jface.action.IMenuListener; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.dialogs.IMessageProvider; import org.eclipse.jface.viewers.ColumnLabelProvider; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.IDoubleClickListener; import org.eclipse.jface.viewers.IStructuredContentProvider; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.TableViewerColumn; import org.eclipse.jface.viewers.Viewer; import org.eclipse.swt.SWT; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.layout.FillLayout; 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.Label; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PartInitException; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.browser.IWebBrowser; import org.eclipse.ui.browser.IWorkbenchBrowserSupport; import org.eclipse.ui.forms.IManagedForm; import org.eclipse.ui.forms.editor.FormEditor; import org.eclipse.ui.forms.editor.FormPage; import org.eclipse.ui.forms.events.HyperlinkEvent; import org.eclipse.ui.forms.widgets.FormToolkit; import org.eclipse.ui.forms.widgets.Hyperlink; import org.eclipse.ui.forms.widgets.ScrolledForm; /** * Show all modules contained in a given modular distribution as filter-able * table */ public class ModularDistVersionOverviewPage extends FormPage implements SlcNames { final static String PAGE_ID = "ModularDistVersionOverviewPage"; // Business Objects private Node modularDistribution; // private Node modularDistributionBase; // This page widgets private NodeViewerComparator comparator; private TableViewer viewer; private FormToolkit tk; private Text filterTxt; private final static String FILTER_HELP_MSG = "Enter filter criterion separated by a space"; public ModularDistVersionOverviewPage(FormEditor formEditor, String title, Node modularDistribution) { super(formEditor, PAGE_ID, title); this.modularDistribution = modularDistribution; } @Override protected void createFormContent(IManagedForm managedForm) { // General settings for this page ScrolledForm form = managedForm.getForm(); tk = managedForm.getToolkit(); Composite body = form.getBody(); GridLayout layout = new GridLayout(1, false); layout.marginWidth = 5; layout.marginRight = 15; layout.verticalSpacing = 0; body.setLayout(layout); try { form.setText(modularDistribution.hasProperty(SlcNames.SLC_NAME) ? modularDistribution .getProperty(SlcNames.SLC_NAME).getString() : ""); form.setMessage( modularDistribution .hasProperty(SlcNames.SLC_BUNDLE_DESCRIPTION) ? modularDistribution .getProperty(SlcNames.SLC_BUNDLE_DESCRIPTION) .getString() : "", IMessageProvider.NONE); } catch (RepositoryException re) { throw new SlcException("Unable to get bundle name for node " + modularDistribution, re); } // Main layout Composite header = tk.createComposite(body); header.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false)); populateHeaderPart(header); Composite moduleTablePart = tk.createComposite(body); moduleTablePart.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); populateModuleTablePart(moduleTablePart); } private void populateHeaderPart(Composite parent) { GridLayout layout = new GridLayout(6, false); // layout.marginWidth = layout.horizontalSpacing = layout.marginHeight = // 0; layout.horizontalSpacing = 10; parent.setLayout(layout); try { // 1st Line: Category, name version createLT( parent, "Category", modularDistribution.hasProperty(SlcNames.SLC_CATEGORY) ? modularDistribution .getProperty(SlcNames.SLC_CATEGORY).getString() : ""); createLT( parent, "Name", modularDistribution.hasProperty(SlcNames.SLC_NAME) ? modularDistribution .getProperty(SlcNames.SLC_NAME).getString() : ""); createLT( parent, "Version", modularDistribution.hasProperty(SlcNames.SLC_VERSION) ? modularDistribution .getProperty(SlcNames.SLC_VERSION).getString() : ""); // 2nd Line: Vendor, licence, sources createLT( parent, "Vendor", modularDistribution .hasProperty(DistConstants.SLC_BUNDLE_VENDOR) ? modularDistribution .getProperty(DistConstants.SLC_BUNDLE_VENDOR) .getString() : "N/A"); createHyperlink(parent, "Licence", DistConstants.SLC_BUNDLE_LICENCE); addSourceSourcesLink(parent); } catch (RepositoryException re) { throw new SlcException("Unable to get bundle name for node " + modularDistribution, re); } } private Text createLT(Composite parent, String labelValue, String textValue) { Label label = tk.createLabel(parent, labelValue, SWT.RIGHT); // label.setFont(EclipseUiUtils.getBoldFont(parent)); label.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false)); // Add a trailing space to workaround a display glitch in RAP 1.3 Text text = tk.createText(parent, textValue + " ", SWT.LEFT); text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); text.setEditable(false); return text; } private void createHyperlink(Composite parent, String label, String jcrPropName) throws RepositoryException { tk.createLabel(parent, label, SWT.NONE); if (modularDistribution.hasProperty(jcrPropName)) { final Hyperlink link = tk.createHyperlink(parent, modularDistribution.getProperty(jcrPropName).getString(), SWT.NONE); link.addHyperlinkListener(new AbstractHyperlinkListener() { @Override public void linkActivated(HyperlinkEvent e) { try { IWorkbenchBrowserSupport browserSupport = PlatformUI .getWorkbench().getBrowserSupport(); IWebBrowser browser = browserSupport .createBrowser( IWorkbenchBrowserSupport.LOCATION_BAR | IWorkbenchBrowserSupport.NAVIGATION_BAR, "SLC Distribution browser", "SLC Distribution browser", "A tool tip"); browser.openURL(new URL(link.getText())); } catch (Exception ex) { throw new SlcException("error opening browser", ex); //$NON-NLS-1$ } } }); } else tk.createLabel(parent, "N/A", SWT.NONE); } // helper to check if sources are available private void addSourceSourcesLink(Composite parent) { try { String srcPath = RepoUtils.relatedPdeSourcePath( RepoConstants.DEFAULT_ARTIFACTS_BASE_PATH, modularDistribution); if (!modularDistribution.getSession().nodeExists(srcPath)) { createLT(parent, "Sources", "N/A"); } else { Node sourcesNode = modularDistribution.getSession().getNode( srcPath); String srcName = null; if (sourcesNode.hasProperty(SlcNames.SLC_SYMBOLIC_NAME)) srcName = sourcesNode.getProperty( SlcNames.SLC_SYMBOLIC_NAME).getString(); else srcName = sourcesNode.getName(); Label label = tk.createLabel(parent, "Sources", SWT.RIGHT); label.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false)); final Hyperlink link = tk.createHyperlink(parent, srcName, SWT.NONE); link.addHyperlinkListener(new AbstractHyperlinkListener() { @Override public void linkActivated(HyperlinkEvent e) { try { System.out.println("CLICK on Sources link"); } catch (Exception ex) { throw new SlcException("error opening browser", ex); //$NON-NLS-1$ } } }); } } catch (RepositoryException e) { throw new SlcException("Unable to configure sources link for " + modularDistribution, e); } } private void populateModuleTablePart(Composite parent) { GridLayout layout = new GridLayout(1, false); layout.marginWidth = layout.horizontalSpacing = 0; layout.verticalSpacing = 5; layout.marginTop = 15; parent.setLayout(layout); // A sub title Label label = tk.createLabel(parent, "Modules included in the current distribution", SWT.NONE); label.setFont(EclipseUiUtils.getBoldFont(parent)); // Add the filter section Composite filterPart = tk.createComposite(parent); filterPart.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false)); createFilterPart(filterPart); // Add the table Composite tablePart = tk.createComposite(parent); tablePart.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); createTableViewer(tablePart); // populate it on first pass. refresh(); } private void createFilterPart(Composite parent) { GridLayout layout = new GridLayout(2, false); layout.marginWidth = layout.marginHeight = layout.verticalSpacing = 0; layout.horizontalSpacing = 5; parent.setLayout(layout); // Text Area to filter filterTxt = tk.createText(parent, "", SWT.BORDER | SWT.SINGLE | SWT.SEARCH | SWT.CANCEL); filterTxt.setMessage(FILTER_HELP_MSG); filterTxt.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); filterTxt.addModifyListener(new ModifyListener() { public void modifyText(ModifyEvent event) { refresh(); } }); Button resetBtn = tk.createButton(parent, null, SWT.PUSH); resetBtn.setImage(DistImages.IMG_CLEAR); resetBtn.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { filterTxt.setText(""); filterTxt.setMessage(FILTER_HELP_MSG); } }); } private void createTableViewer(Composite parent) { parent.setLayout(new FillLayout()); // helpers to enable sorting by column List propertiesList = new ArrayList(); List propertyTypesList = new ArrayList(); // Define the TableViewer viewer = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.BORDER); // Name TableViewerColumn col = new TableViewerColumn(viewer, SWT.NONE); col.getColumn().setWidth(220); col.getColumn().setText("Category"); col.setLabelProvider(new ColumnLabelProvider() { @Override public String getText(Object element) { return JcrUtils.get((Node) element, SlcNames.SLC_CATEGORY); } }); col.getColumn().addSelectionListener(getSelectionAdapter(0)); propertiesList.add(SlcNames.SLC_CATEGORY); propertyTypesList.add(PropertyType.STRING); // Symbolic name col = new TableViewerColumn(viewer, SWT.NONE); col.getColumn().setWidth(220); col.getColumn().setText("Name"); col.setLabelProvider(new ColumnLabelProvider() { @Override public String getText(Object element) { return JcrUtils.get((Node) element, SLC_NAME); } }); col.getColumn().addSelectionListener(getSelectionAdapter(1)); propertiesList.add(SLC_NAME); propertyTypesList.add(PropertyType.STRING); // Version col = new TableViewerColumn(viewer, SWT.NONE); col.getColumn().setWidth(160); col.getColumn().setText("Version"); col.setLabelProvider(new ColumnLabelProvider() { @Override public String getText(Object element) { return JcrUtils.get((Node) element, SLC_VERSION); } }); col.getColumn().addSelectionListener(getSelectionAdapter(2)); propertiesList.add(SLC_VERSION); propertyTypesList.add(PropertyType.STRING); final Table table = viewer.getTable(); table.setHeaderVisible(true); table.setLinesVisible(true); viewer.setContentProvider(new DistributionsContentProvider()); getSite().setSelectionProvider(viewer); comparator = new NodeViewerComparator(2, NodeViewerComparator.ASCENDING, propertiesList, propertyTypesList); viewer.setComparator(comparator); // Context Menu MenuManager menuManager = new MenuManager(); Menu menu = menuManager.createContextMenu(viewer.getTable()); menuManager.addMenuListener(new IMenuListener() { public void menuAboutToShow(IMenuManager manager) { contextMenuAboutToShow(manager); } }); viewer.getTable().setMenu(menu); getSite().registerContextMenu(menuManager, viewer); // Double click viewer.addDoubleClickListener(new DoubleClickListener()); } private void refresh() { final List result = JcrUtils .nodeIteratorToList(listBundleArtifacts()); viewer.setInput(result); } /** Build repository request */ private NodeIterator listBundleArtifacts() { try { Session session = modularDistribution.getSession(); QueryManager queryManager = session.getWorkspace() .getQueryManager(); QueryObjectModelFactory factory = queryManager.getQOMFactory(); Selector source = factory.selector(SlcTypes.SLC_MODULE_COORDINATES, SlcTypes.SLC_MODULE_COORDINATES); // Create a dynamic operand for each property on which we want to // filter DynamicOperand catDO = factory.propertyValue( source.getSelectorName(), SlcNames.SLC_CATEGORY); DynamicOperand nameDO = factory.propertyValue( source.getSelectorName(), SlcNames.SLC_NAME); DynamicOperand versionDO = factory.propertyValue( source.getSelectorName(), SlcNames.SLC_VERSION); String path = modularDistribution.getPath() + "/" + SlcNames.SLC_MODULES; // Default Constraint: correct children Constraint defaultC = factory.descendantNode( source.getSelectorName(), path); String filter = filterTxt.getText(); // Build constraints based the textArea content if (filter != null && !"".equals(filter.trim())) { // Parse the String String[] strs = filter.trim().split(" "); for (String token : strs) { token = token.replace('*', '%'); StaticOperand so = factory.literal(session .getValueFactory().createValue("%" + token + "%")); Constraint currC = factory.comparison(catDO, QueryObjectModelFactory.JCR_OPERATOR_LIKE, so); currC = factory.or(currC, factory.comparison(versionDO, QueryObjectModelFactory.JCR_OPERATOR_LIKE, so)); currC = factory.or(currC, factory.comparison(nameDO, QueryObjectModelFactory.JCR_OPERATOR_LIKE, so)); defaultC = factory.and(defaultC, currC); } } QueryObjectModel query = factory.createQuery(source, defaultC, null, null); QueryResult result = query.execute(); return result.getNodes(); } catch (RepositoryException re) { throw new SlcException("Unable to refresh module list for node " + modularDistribution, re); } } @Override public void setFocus() { viewer.getTable().setFocus(); } /** Programmatically configure the context menu */ protected void contextMenuAboutToShow(IMenuManager menuManager) { IWorkbenchWindow window = DistPlugin.getDefault().getWorkbench() .getActiveWorkbenchWindow(); // Build conditions // Delete selected artifacts CommandUtils.refreshCommand(menuManager, window, DeleteArtifacts.ID, DeleteArtifacts.DEFAULT_LABEL, DeleteArtifacts.DEFAULT_ICON, true); } private SelectionAdapter getSelectionAdapter(final int index) { SelectionAdapter selectionAdapter = new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { Table table = viewer.getTable(); comparator.setColumn(index); int dir = table.getSortDirection(); if (table.getSortColumn() == table.getColumn(index)) { dir = dir == SWT.UP ? SWT.DOWN : SWT.UP; } else { dir = SWT.DOWN; } table.setSortDirection(dir); table.setSortColumn(table.getColumn(index)); viewer.refresh(); } }; return selectionAdapter; } /* LOCAL CLASSES */ private class DistributionsContentProvider implements IStructuredContentProvider { // we keep a cache of the Nodes in the content provider to be able to // manage long request private List nodes; public void dispose() { } // We expect a list of nodes as a new input @SuppressWarnings("unchecked") public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { nodes = (List) newInput; } public Object[] getElements(Object arg0) { return nodes.toArray(); } } private class DoubleClickListener implements IDoubleClickListener { public void doubleClick(DoubleClickEvent event) { Object obj = ((IStructuredSelection) event.getSelection()) .getFirstElement(); try { if (obj instanceof Node) { Node node = (Node) obj; if (node.isNodeType(SlcTypes.SLC_BUNDLE_ARTIFACT)) { GenericBundleEditorInput gaei = new GenericBundleEditorInput( node); DistPlugin.getDefault().getWorkbench() .getActiveWorkbenchWindow().getActivePage() .openEditor(gaei, GenericBundleEditor.ID); } } } catch (RepositoryException re) { throw new ArgeoException( "Repository error while getting node info", re); } catch (PartInitException pie) { throw new ArgeoException( "Unexepected exception while opening artifact editor", pie); } } } // /** // * UI Trick to put scroll bar on the table rather than on the scrollform // */ // private void refreshLayout() { // // Compute desired table size // int maxH = getManagedForm().getForm().getSize().y; // int maxW = getManagedForm().getForm().getParent().getSize().x; // maxH = maxH - header.getSize().y; // final Table table = viewer.getTable(); // GridData gd = new GridData(SWT.LEFT, SWT.TOP, true, true); // // // when table height is less than 200 px, we let the scroll bar on the // // scrollForm // // FIXME substract some spare space. There is room here for optimization // gd.heightHint = Math.max(maxH - 35, 200); // gd.widthHint = Math.max(maxW - 35, 200); // // table.setLayoutData(gd); // getManagedForm().reflow(true); // } @Override public void setActive(boolean active) { super.setActive(active); if (active) { // refreshLayout(); } } }