+/*******************************************************************************
+ * Copyright (c) 2016 Ericsson
+ *
+ * All rights reserved. This program and the accompanying materials are
+ * made available under the terms of the Eclipse Public License v1.0 which
+ * accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Patrick Tasse - Initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.tracecompass.tmf.ui.viewers.events;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.CLabel;
+import org.eclipse.swt.events.ControlAdapter;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.RowLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.tracecompass.internal.tmf.ui.Activator;
+import org.eclipse.tracecompass.tmf.core.filter.ITmfFilter;
+import org.eclipse.tracecompass.tmf.core.filter.model.ITmfFilterTreeNode;
+import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterNode;
+import org.eclipse.tracecompass.tmf.core.filter.model.TmfFilterRootNode;
+
+/**
+ * Header bar for the events table.
+ *
+ * @since 2.0
+ */
+public class TmfEventsTableHeader extends Composite {
+
+ private static final Image COLLAPSED = Activator.getDefault().getImageFromPath("icons/ovr16/collapsed_ovr.gif"); //$NON-NLS-1$
+ private static final Image EXPANDED = Activator.getDefault().getImageFromPath("icons/ovr16/expanded_ovr.gif"); //$NON-NLS-1$
+ private static final Image DELETE = Activator.getDefault().getImageFromPath("icons/elcl16/delete_button.gif"); //$NON-NLS-1$
+ private static final Image DELETE_SMALL = Activator.getDefault().getImageFromPath("icons/ovr16/delete_ovr.gif"); //$NON-NLS-1$
+ private static final int DEFAULT_MARGIN = 3;
+ private static final int COLLAPSED_IMAGE_MARGIN = 2;
+ private static final int COLLAPSED_RIGHT_MARGIN = 32;
+ private static final RGB LABEL_BACKGROUND = new RGB(255, 255, 192);
+ private static final String TOOLTIP_KEY = "toolTip"; //$NON-NLS-1$
+
+ /**
+ * Interface for header bar call-backs.
+ */
+ public interface IEventsTableHeaderListener {
+ /**
+ * A filter has been selected.
+ *
+ * @param filter
+ * the selected filter
+ */
+ void filterSelected(ITmfFilter filter);
+
+ /**
+ * A filter has been removed.
+ *
+ * @param filter
+ * the removed filter
+ */
+ void filterRemoved(ITmfFilter filter);
+ }
+
+ private final IEventsTableHeaderListener fListener;
+ private final RowLayout fLayout;
+ private final Color fLabelBackground;
+ private boolean fCollapsed = false;
+
+ /**
+ * Constructor
+ *
+ * @param parent
+ * the parent composite
+ * @param style
+ * the style of widget to construct
+ * @param listener
+ * the listener to the header bar events
+ */
+ public TmfEventsTableHeader(Composite parent, int style, IEventsTableHeaderListener listener) {
+ super(parent, style);
+ fListener = listener;
+ fLayout = new RowLayout();
+ fLayout.marginTop = 0;
+ fLayout.marginBottom = 0;
+ fLayout.marginLeft = EXPANDED.getBounds().width;
+ setLayout(fLayout);
+ fLabelBackground = new Color(getDisplay(), LABEL_BACKGROUND);
+ getParent().addControlListener(new ControlAdapter() {
+ @Override
+ public void controlResized(ControlEvent e) {
+ getParent().layout();
+ }
+ });
+ addPaintListener(e -> {
+ if (fCollapsed) {
+ e.gc.drawImage(COLLAPSED, 0, 0);
+ } else {
+ e.gc.drawImage(EXPANDED, 0, 0);
+ }
+ });
+ addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseDown(MouseEvent e) {
+ toggle();
+ }
+ });
+ }
+
+ @Override
+ public void dispose() {
+ super.dispose();
+ fLabelBackground.dispose();
+ }
+
+ @Override
+ public Point computeSize(int wHint, int hHint, boolean changed) {
+ int height = fCollapsed && getChildren().length > 0 ? EXPANDED.getBounds().height : hHint;
+ return super.computeSize(getParent().getSize().x, height, changed);
+ }
+
+ /**
+ * Add a filter to the header.
+ *
+ * @param filter
+ * the filter to add
+ */
+ public void addFilter(ITmfFilter filter) {
+ if (filter instanceof TmfFilterRootNode) {
+ TmfFilterRootNode parentFilter = (TmfFilterRootNode) filter;
+ for (ITmfFilterTreeNode childFilter : parentFilter.getChildren()) {
+ addNewFilter(childFilter);
+ }
+ } else {
+ addNewFilter(filter);
+ }
+ fLayout.marginTop = 1;
+ fLayout.marginBottom = 1;
+ getParent().layout(true, true);
+ }
+
+ /**
+ * Remove a filter from the header.
+ *
+ * @param filter
+ * the filter to remove
+ */
+ public void removeFilter(ITmfFilter filter) {
+ for (Control control : getChildren()) {
+ if (filter.equals(control.getData())) {
+ control.dispose();
+ break;
+ }
+ }
+ if (getChildren().length == 0) {
+ fLayout.marginTop = 0;
+ fLayout.marginBottom = 0;
+ }
+ getParent().layout(true, true);
+ }
+
+ /**
+ * Clear all filters in the header.
+ */
+ public void clearFilters() {
+ for (Control control : getChildren()) {
+ control.dispose();
+ }
+ fLayout.marginTop = 0;
+ fLayout.marginBottom = 0;
+ getParent().layout(true, true);
+ }
+
+ private void addNewFilter(ITmfFilter filter) {
+ CLabel label = new CLabel(this, SWT.SHADOW_OUT);
+ label.setBackground(fLabelBackground);
+ String text;
+ if (filter instanceof TmfFilterNode) {
+ text = ((TmfFilterNode) filter).getFilterName();
+ label.setData(TOOLTIP_KEY, filter.toString());
+ } else {
+ text = filter.toString();
+ }
+ if (fCollapsed) {
+ label.setToolTipText(text);
+ label.setTopMargin(0);
+ label.setBottomMargin(0);
+ label.setRightMargin(COLLAPSED_RIGHT_MARGIN);
+ } else {
+ label.setImage(DELETE);
+ label.setText(text);
+ label.setToolTipText((String) label.getData(TOOLTIP_KEY));
+ }
+ label.setData(filter);
+ label.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseDown(MouseEvent e) {
+ Rectangle bounds;
+ if (fCollapsed) {
+ bounds = new Rectangle(0, 0, 2 * COLLAPSED_IMAGE_MARGIN + DELETE_SMALL.getBounds().width, label.getBounds().height);
+ } else {
+ bounds = DELETE.getBounds();
+ bounds.x += label.getLeftMargin();
+ bounds.y = (label.getSize().y - bounds.height) / 2;
+ }
+ if (bounds.contains(e.x, e.y)) {
+ fListener.filterRemoved((ITmfFilter) label.getData());
+ } else {
+ fListener.filterSelected((ITmfFilter) label.getData());
+ getParent().layout(true, true);
+ }
+ }
+ });
+ label.addPaintListener(e -> {
+ if (fCollapsed) {
+ e.gc.drawImage(DELETE_SMALL, COLLAPSED_IMAGE_MARGIN, COLLAPSED_IMAGE_MARGIN);
+ }
+ });
+ }
+
+ private void toggle() {
+ fCollapsed = !fCollapsed;
+ for (Control child : getChildren()) {
+ if (child instanceof CLabel) {
+ CLabel label = (CLabel) child;
+ if (fCollapsed) {
+ label.setImage(null);
+ label.setToolTipText(label.getText());
+ label.setText(null);
+ label.setMargins(DEFAULT_MARGIN, 0, COLLAPSED_RIGHT_MARGIN, 0);
+ } else {
+ label.setImage(DELETE);
+ label.setText(label.getToolTipText());
+ label.setToolTipText((String) label.getData(TOOLTIP_KEY));
+ label.setMargins(DEFAULT_MARGIN, DEFAULT_MARGIN, DEFAULT_MARGIN, DEFAULT_MARGIN);
+ }
+ }
+ }
+ getParent().layout();
+ }
+}