| /******************************************************************************* |
| * Copyright (c) 2016 ALL4TEC & CEA LIST. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * ALL4TEC & CEA LIST - initial API and implementation |
| ******************************************************************************/ |
| package org.polarsys.esf.core.common.ui.widget; |
| |
| import org.apache.commons.lang3.StringUtils; |
| import org.eclipse.jface.action.IMenuListener; |
| import org.eclipse.jface.action.IMenuManager; |
| import org.eclipse.jface.action.MenuManager; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.dnd.DND; |
| import org.eclipse.swt.dnd.DropTarget; |
| import org.eclipse.swt.dnd.DropTargetAdapter; |
| import org.eclipse.swt.dnd.DropTargetEvent; |
| import org.eclipse.swt.dnd.TextTransfer; |
| import org.eclipse.swt.dnd.Transfer; |
| import org.eclipse.swt.events.KeyAdapter; |
| import org.eclipse.swt.events.KeyEvent; |
| import org.eclipse.swt.events.ModifyEvent; |
| import org.eclipse.swt.events.ModifyListener; |
| import org.eclipse.swt.events.MouseAdapter; |
| import org.eclipse.swt.events.MouseEvent; |
| import org.eclipse.swt.events.MouseTrackAdapter; |
| import org.eclipse.swt.graphics.Point; |
| import org.eclipse.swt.graphics.Rectangle; |
| import org.eclipse.swt.layout.GridData; |
| import org.eclipse.swt.layout.GridLayout; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Label; |
| import org.eclipse.swt.widgets.Menu; |
| import org.eclipse.swt.widgets.Text; |
| import org.polarsys.esf.core.common.ui.CommonUIActivator; |
| import org.polarsys.esf.core.common.ui.CommonUIActivator.Implementation; |
| import org.polarsys.esf.core.common.ui.actions.FilterPartAction; |
| import org.polarsys.esf.core.common.ui.constants.ImageConstants; |
| import org.polarsys.esf.core.common.ui.filter.FilterPartManager; |
| import org.polarsys.esf.core.common.ui.filter.IFilterPartListener; |
| |
| /** |
| * Composite used to display all what is needed for the filter mechanisms. |
| * This include the field to select the filter value, the menu to apply |
| * to filter on available parts, etc. |
| * |
| * @author $Author: jdumont $ |
| * @version $Revision: 83 $ |
| */ |
| public class FilterComposite |
| extends Composite { |
| |
| /** The preferred width for the composite. */ |
| private static final int CONTROL_WIDTH = 200; |
| |
| /** The preferred height for the composite. */ |
| private static final int CONTROL_HEIGHT = 17; |
| |
| /** Reference to the singleton which manages the filter mechanisms. */ |
| private static final FilterPartManager FILTER_PART_MANAGER = FilterPartManager.INSTANCE; |
| |
| /** The filter text field. */ |
| private Text mFilterTextField = null; |
| |
| /** The clear label. */ |
| private Label mClearLabel = null; |
| |
| /** The search label. */ |
| private Label mSearchLabel = null; |
| |
| /** |
| * Default constructor. |
| * |
| * @param pParent The parent composite |
| */ |
| public FilterComposite(final Composite pParent) { |
| // Call the parent constructor |
| super(pParent, SWT.FLAT); |
| |
| // Create its content |
| createContent(pParent); |
| } |
| |
| /** |
| * Create the content of the composite, with all the needed widgets. |
| * |
| * @param pParent The parent composite |
| */ |
| private void createContent(final Composite pParent) { |
| // Initialise the layout of this instance |
| final GridLayout vMainLayout = new GridLayout(); |
| vMainLayout.marginHeight = 0; |
| vMainLayout.marginWidth = 1; |
| setLayout(vMainLayout); |
| |
| // Create the filter composite |
| final Composite vFilterComposite = new Composite(this, SWT.BORDER); |
| vFilterComposite.setBackground(getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND)); |
| |
| // Initialise the layout of the filter composite to display the widgets on three columns |
| final GridLayout vFilterLayout = new GridLayout(3, false); |
| vFilterLayout.marginHeight = 0; |
| vFilterLayout.marginWidth = 0; |
| vFilterComposite.setLayout(vFilterLayout); |
| GridData vGridData = new GridData(SWT.FILL, SWT.BEGINNING, true, false); |
| vGridData.widthHint = CONTROL_WIDTH; |
| vFilterComposite.setLayoutData(vGridData); |
| |
| // Create the search label |
| createSearchLabel(vFilterComposite); |
| |
| // Create the menu manager and associate it to the search label |
| createMenuManager(); |
| |
| // Create the filter text field |
| createFilterText(vFilterComposite); |
| |
| // Create the clear label |
| createClearLabel(vFilterComposite); |
| |
| // Initialise the widgets value for the initial state, without filter value |
| mClearLabel.setVisible(false); |
| } |
| |
| /** |
| * Create the filter text field. |
| * |
| * @param pParent The parent composite |
| */ |
| private void createFilterText(final Composite pParent) { |
| // Create and initialise the filter text |
| mFilterTextField = new Text(pParent, SWT.SINGLE | SWT.ICON_CANCEL); |
| mFilterTextField.setMessage(CommonUIActivator.getMessages().getString("FilterComposite.text.defaulttext")); //$NON-NLS-1$ |
| mFilterTextField.setToolTipText(CommonUIActivator.getMessages().getString("FilterComposite.text.tooltip")); //$NON-NLS-1$ |
| |
| // Add a key listener on the field to be warned that |
| // the filter value changes dynamically |
| mFilterTextField.addKeyListener(new KeyAdapter() { |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void keyReleased(final KeyEvent pEvent) { |
| FILTER_PART_MANAGER.updateFilterText(mFilterTextField.getText()); |
| } |
| }); |
| |
| // Add a modify listener on the field to react |
| // when a value is validated |
| mFilterTextField.addModifyListener(new ModifyListener() { |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void modifyText(final ModifyEvent pEvent) { |
| mClearLabel.setVisible(StringUtils.isNotEmpty(mFilterTextField.getText())); |
| } |
| }); |
| |
| // Build the layout data for the text field |
| // NB : If the text widget supported cancel then it will have it's own |
| // integrated button, thus we can take the whole space |
| final GridData vGridData = new GridData(SWT.FILL, SWT.CENTER, true, false); |
| vGridData.heightHint = CONTROL_HEIGHT; |
| if ((mFilterTextField.getStyle() & SWT.ICON_CANCEL) != 0) { |
| vGridData.horizontalSpan = 2; |
| } |
| mFilterTextField.setLayoutData(vGridData); |
| |
| // Add the drag and drop support on the text field |
| final DropTarget vDropTarget = new DropTarget(mFilterTextField, DND.DROP_MOVE | DND.DROP_COPY); |
| vDropTarget.setTransfer(new Transfer[] {TextTransfer.getInstance()}); |
| vDropTarget.addDropListener(new DropTargetAdapter() { |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void drop(final DropTargetEvent pEvent) { |
| // Try to use the event data as new filter text |
| mFilterTextField.setText(String.valueOf(pEvent.data)); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void dragEnter(final DropTargetEvent pEvent) { |
| // Force the drag and drop operation to be a copy |
| pEvent.detail = DND.DROP_COPY; |
| } |
| }); |
| } |
| |
| /** |
| * Create the search label, displayed as an image, and associate a context menu to it. |
| * |
| * @param pParent The parent composite |
| */ |
| private void createSearchLabel(final Composite pParent) { |
| // Create the label and set its layout |
| mSearchLabel = new Label(pParent, SWT.NONE); |
| mSearchLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 0, 0)); |
| |
| // Set the label display, to show a standard search image |
| mSearchLabel.setImage(CommonUIActivator.getPlugin().getImageRegistry().get(Implementation.ICON_SEARCH_KEY)); |
| mSearchLabel.setBackground(pParent.getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND)); |
| mSearchLabel.setToolTipText(CommonUIActivator.getMessages().getString("FilterComposite.search.tooltip")); //$NON-NLS-1$ |
| } |
| |
| /** |
| * Create the menu manager of the search label. |
| * Thus, when the user click on the search label, a contextual menu is |
| * displayed with all the available listeners. |
| */ |
| private void createMenuManager() { |
| // Create the menu manager |
| final MenuManager vMenuManager = new MenuManager(); |
| |
| // Ensure that when the menu is shown, all the previous elements are removed |
| vMenuManager.setRemoveAllWhenShown(true); |
| |
| // Add a custom listener to populate the menu dynamically |
| vMenuManager.addMenuListener(new IMenuListener() { |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void menuAboutToShow(final IMenuManager pMenuManager) { |
| // Add an item in the menu for all the active listeners |
| // This item will be already checked |
| for (IFilterPartListener vFiltrePartListener : FILTER_PART_MANAGER.getActivePartListenersList()) { |
| pMenuManager.add(new FilterPartAction(vFiltrePartListener, true)); |
| } |
| |
| // Add an item in the menu for all the available listeners |
| // This item won't be checked |
| for (IFilterPartListener vFiltrePartListener : FILTER_PART_MANAGER.getAvailablePartListenersList()) { |
| pMenuManager.add(new FilterPartAction(vFiltrePartListener)); |
| } |
| } |
| }); |
| |
| // Add a custom listener to the search label, to be able to react on the mouse click, |
| // and build the contextual menu to the right location |
| mSearchLabel.addMouseListener(new MouseAdapter() { |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void mouseDown(final MouseEvent pEvent) { |
| // Ensure that it's a left click |
| if (pEvent.button == 1) { |
| // Create the context menu for the search label |
| final Menu vContextMenu = vMenuManager.createContextMenu(mSearchLabel); |
| |
| // Build the point, relative to the search label, where the |
| // contextual menu must appear |
| final Rectangle vRectangle = mSearchLabel.getBounds(); |
| Point vPoint = new Point(vRectangle.x, vRectangle.y + vRectangle.height); |
| vPoint = mSearchLabel.toDisplay(vPoint); |
| |
| // Set the menu location, and display it |
| vContextMenu.setLocation(vPoint.x, vPoint.y); |
| vContextMenu.setVisible(true); |
| } |
| } |
| }); |
| } |
| |
| /** |
| * Create the clear text label, displayed as an image. |
| * |
| * @param pParent The parent composite |
| */ |
| private void createClearLabel(final Composite pParent) { |
| // Create the label only if the text widget doesn't support one natively |
| if ((mFilterTextField.getStyle() & SWT.ICON_CANCEL) == 0) { |
| |
| // Create the label and set its layout |
| mClearLabel = new Label(pParent, SWT.NONE); |
| mClearLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false)); |
| |
| // Set the label display, to show a standard clear image |
| mClearLabel.setImage(ImageConstants.ICON_CLEAR_DISABLED); |
| mClearLabel.setBackground(pParent.getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND)); |
| mClearLabel |
| .setToolTipText(CommonUIActivator.getMessages().getString("FilterComposite.clearfilter.tooltip")); //$NON-NLS-1$ |
| |
| // Add a custom mouse listener to be able to clear the text on a click |
| // and to update the label image with the click action |
| mClearLabel.addMouseListener(new ClearLabelMouseAdapter()); |
| |
| // Add a custom mouse track adapter to update the label image when |
| // the mouse is over it |
| mClearLabel.addMouseTrackListener(new MouseTrackAdapter() { |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void mouseEnter(final MouseEvent pEvent) { |
| mClearLabel.setImage(ImageConstants.ICON_CLEAR); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void mouseExit(final MouseEvent pEvent) { |
| mClearLabel.setImage(ImageConstants.ICON_CLEAR_DISABLED); |
| } |
| }); |
| } |
| } |
| |
| /** |
| * Clear the filter text, by setting the default value. |
| */ |
| private void clearFilterText() { |
| // Reset the text from the text widget, using the default filter |
| mFilterTextField.setText(FilterPartManager.DEFAULT_FILTER_TEXT); |
| |
| // Reset the filter text applied by the manager |
| // NB : This will warn all the listeners |
| FILTER_PART_MANAGER.resetFilterText(); |
| } |
| |
| /** |
| * Mouse adapter linked to the clear label. |
| * |
| * The clear label is displayed as an image, and when the user click on it, |
| * it must clear the filter text. |
| * |
| * @author $Author: jdumont $ |
| * @version $Revision: 83 $ |
| */ |
| private class ClearLabelMouseAdapter |
| extends MouseAdapter { |
| |
| /** |
| * Default constructor. |
| */ |
| ClearLabelMouseAdapter() { |
| super(); |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * Update the clear label image. |
| */ |
| @Override |
| public void mouseDown(final MouseEvent pEvent) { |
| mClearLabel.setImage(ImageConstants.ICON_CLEAR_PRESSED); |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * Update the clear label image and clear the filter text. |
| */ |
| @Override |
| public void mouseUp(final MouseEvent pEvent) { |
| // Thus update the label image and clear the text if needed |
| mClearLabel.setImage(ImageConstants.ICON_CLEAR); |
| clearFilterText(); |
| mFilterTextField.setFocus(); |
| } |
| } |
| } |