1 /* 2 * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. 3 * Copyright (c) 2019, Red Hat Inc. All rights reserved. 4 * 5 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 6 * 7 * The contents of this file are subject to the terms of either the Universal Permissive License 8 * v 1.0 as shown at http://oss.oracle.com/licenses/upl 9 * 10 * or the following license: 11 * 12 * Redistribution and use in source and binary forms, with or without modification, are permitted 13 * provided that the following conditions are met: 14 * 15 * 1. Redistributions of source code must retain the above copyright notice, this list of conditions 16 * and the following disclaimer. 17 * 18 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of 19 * conditions and the following disclaimer in the documentation and/or other materials provided with 20 * the distribution. 21 * 22 * 3. Neither the name of the copyright holder nor the names of its contributors may be used to 23 * endorse or promote products derived from this software without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR 26 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 27 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 28 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 30 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 31 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY 32 * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 */ 34 package org.openjdk.jmc.flightrecorder.ui.common; 35 36 import java.util.stream.Collectors; 37 38 import org.eclipse.swt.SWT; 39 import org.eclipse.swt.events.ShellAdapter; 40 import org.eclipse.swt.events.ShellEvent; 41 import org.eclipse.swt.graphics.Point; 42 import org.eclipse.swt.graphics.Rectangle; 43 import org.eclipse.swt.layout.GridData; 44 import org.eclipse.swt.layout.GridLayout; 45 import org.eclipse.swt.widgets.Button; 46 import org.eclipse.swt.widgets.Composite; 47 import org.eclipse.swt.widgets.Event; 48 import org.eclipse.swt.widgets.Listener; 49 import org.eclipse.swt.widgets.Shell; 50 import org.openjdk.jmc.common.item.ItemFilters; 51 import org.openjdk.jmc.flightrecorder.ui.common.LaneEditor.LaneDefinition; 52 import org.openjdk.jmc.flightrecorder.ui.messages.internal.Messages; 53 import org.openjdk.jmc.ui.handlers.MCContextMenuManager; 54 55 public class DropdownLaneFilter extends Composite { 56 private static final int EXTRA_SHELL_WIDTH = 100; 57 private static final int SHELL_HEIGHT = 400; 58 private Button dropdownButton; 59 private GridLayout layout; 60 private MCContextMenuManager mm; 61 private Shell shell; 62 private ShellAdapter shellDisposeAdapter; 63 private ThreadGraphLanes lanes; 64 private TypeFilterBuilder filterEditor; 65 66 public DropdownLaneFilter(Composite parent, ThreadGraphLanes lanes, MCContextMenuManager mm) { 67 super(parent, SWT.NO_BACKGROUND); 68 this.lanes = lanes; 69 this.mm = mm; 70 this.layout = createGridLayout(); 71 this.shellDisposeAdapter = createDisposeAdapter(); 72 setLayout(layout); 73 dropdownButton = new Button(this, SWT.TOGGLE); 74 dropdownButton.setLayoutData(new GridData(GridData.FILL_BOTH)); 75 dropdownButton.setText(Messages.DropdownLaneFilter_THREAD_STATE_SELECTION); 76 dropdownButton.addListener(SWT.MouseDown, new Listener() { 77 @Override 78 public void handleEvent(Event event) { 79 if (!dropdownButton.getSelection()) { 80 displayDropdown(); 81 } else { 82 disposeDropdown(); 83 } 84 } 85 }); 86 } 87 88 /** 89 * Creates a new shell which is positioned underneath the dropdown button. 90 * This new shell creates the appearance of a dropdown component, and it's 91 * contents will be the TypeFilterBuilder as found in the Edit Thread Lanes 92 * dialog. 93 */ 94 private void displayDropdown() { 95 Point p = dropdownButton.getParent().toDisplay(dropdownButton.getLocation()); 96 Point size = dropdownButton.getSize(); 97 Rectangle shellRect = new Rectangle(p.x, p.y + size.y, size.x, 0); 98 99 shell = new Shell(DropdownLaneFilter.this.getShell(), SWT.BORDER); 100 shell.setLayout(this.layout); 101 shell.setSize(shellRect.width + EXTRA_SHELL_WIDTH, SHELL_HEIGHT); 102 shell.setLocation(shellRect.x, shellRect.y); 103 shell.addShellListener(this.shellDisposeAdapter); 104 105 filterEditor = new TypeFilterBuilder(shell, this::onTypeFilterChange); 106 filterEditor.setInput(lanes.getTypeTree()); 107 filterEditor.selectTypes(lanes.getEnabledLanes()); 108 filterEditor.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); 109 shell.open(); 110 } 111 112 private void disposeDropdown() { 113 if (shell != null && !shell.isDisposed()) { 114 shell.dispose(); 115 } 116 } 117 118 private GridLayout createGridLayout() { 119 GridLayout layout = new GridLayout(); 120 layout.marginHeight = 0; 121 layout.marginWidth = 0; 122 return layout; 123 } 124 125 /** 126 * Prepares the ShellAdapter which will be used when the dropdown 127 * is to be disposed of. 128 * @return ShellAdapter to be used by the dropdown shell 129 */ 130 private ShellAdapter createDisposeAdapter() { 131 return new ShellAdapter() { 132 public void shellDeactivated(ShellEvent event) { 133 if (dropdownButton.getSelection()) { 134 disposeDropdown(); 135 dropdownButton.setSelection(false); 136 } 137 } 138 }; 139 } 140 141 /** 142 * Creates a new filter, Quick Filter, to be used by the dropdown lane filter. 143 * Sets the quick filter to be active, and update the context menu. 144 */ 145 private void onTypeFilterChange() { 146 LaneDefinition quickLaneDef = new LaneDefinition(Messages.DropdownLaneFilter_QUICK_FILTER, true, 147 ItemFilters.type(filterEditor.getCheckedTypeIds().collect(Collectors.toSet())), false); 148 lanes.useDropdownFilter(quickLaneDef); 149 lanes.updateContextMenu(mm, false); 150 } 151 }