1 /* 2 * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. 3 * 4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5 * 6 * The contents of this file are subject to the terms of either the Universal Permissive License 7 * v 1.0 as shown at http://oss.oracle.com/licenses/upl 8 * 9 * or the following license: 10 * 11 * Redistribution and use in source and binary forms, with or without modification, are permitted 12 * provided that the following conditions are met: 13 * 14 * 1. Redistributions of source code must retain the above copyright notice, this list of conditions 15 * and the following disclaimer. 16 * 17 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of 18 * conditions and the following disclaimer in the documentation and/or other materials provided with 19 * the distribution. 20 * 21 * 3. Neither the name of the copyright holder nor the names of its contributors may be used to 22 * endorse or promote products derived from this software without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR 25 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 26 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 27 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 30 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY 31 * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 package org.openjdk.jmc.flightrecorder.ui.common; 34 35 import java.util.HashMap; 36 import java.util.Iterator; 37 import java.util.List; 38 import java.util.Map; 39 import java.util.Map.Entry; 40 import java.util.Optional; 41 import java.util.Set; 42 import java.util.function.BiConsumer; 43 import java.util.function.Consumer; 44 import java.util.stream.Collectors; 45 46 import org.eclipse.jface.layout.GridDataFactory; 47 import org.eclipse.jface.layout.GridLayoutFactory; 48 import org.eclipse.jface.viewers.ComboViewer; 49 import org.eclipse.jface.viewers.ISelection; 50 import org.eclipse.jface.viewers.ISelectionChangedListener; 51 import org.eclipse.jface.viewers.IStructuredContentProvider; 52 import org.eclipse.jface.viewers.IStructuredSelection; 53 import org.eclipse.jface.viewers.LabelProvider; 54 import org.eclipse.jface.viewers.SelectionChangedEvent; 55 import org.eclipse.jface.viewers.StructuredSelection; 56 import org.eclipse.jface.viewers.Viewer; 57 import org.eclipse.swt.SWT; 58 import org.eclipse.swt.events.PaintEvent; 59 import org.eclipse.swt.events.PaintListener; 60 import org.eclipse.swt.events.SelectionAdapter; 61 import org.eclipse.swt.events.SelectionEvent; 62 import org.eclipse.swt.graphics.Point; 63 import org.eclipse.swt.graphics.RGB; 64 import org.eclipse.swt.widgets.Button; 65 import org.eclipse.swt.widgets.Canvas; 66 import org.eclipse.swt.widgets.Composite; 67 import org.eclipse.swt.widgets.Control; 68 import org.eclipse.swt.widgets.Label; 69 import org.eclipse.ui.forms.widgets.Form; 70 71 import org.openjdk.jmc.common.IDisplayable; 72 import org.openjdk.jmc.common.IMCThread; 73 import org.openjdk.jmc.common.item.IAttribute; 74 import org.openjdk.jmc.common.item.IItemCollection; 75 import org.openjdk.jmc.common.item.IItemFilter; 76 import org.openjdk.jmc.common.unit.IQuantity; 77 import org.openjdk.jmc.common.unit.IRange; 78 import org.openjdk.jmc.flightrecorder.ui.IPageContainer; 79 import org.openjdk.jmc.flightrecorder.ui.messages.internal.Messages; 80 import org.openjdk.jmc.flightrecorder.ui.selection.FlavorToolkit; 81 import org.openjdk.jmc.flightrecorder.ui.selection.IFlavoredSelection; 82 import org.openjdk.jmc.flightrecorder.ui.selection.IItemStreamFlavor; 83 import org.openjdk.jmc.flightrecorder.ui.selection.SelectionStore; 84 import org.openjdk.jmc.flightrecorder.ui.selection.SelectionStore.SelectionStoreEntry; 85 import org.openjdk.jmc.flightrecorder.ui.selection.SelectionStore.SelectionStoreListener; 86 import org.openjdk.jmc.ui.charts.SubdividedQuantityRange; 87 import org.openjdk.jmc.ui.misc.SWTColorToolkit; 88 89 /** 90 * Class for creating a flavor chooser. 91 */ 92 public class FlavorSelector implements SelectionStoreListener { 93 94 public static class FlavorSelectorState { 95 96 private boolean showConcurrent = false; 97 private boolean concurrentContained = false; 98 private boolean sameThreads = true; 99 private Map<IFlavoredSelection, IItemStreamFlavor[]> calculatedFlavorsMap = new HashMap<>(); 100 private Map<IFlavoredSelection, IItemStreamFlavor> selectedFlavorMap = new HashMap<>(); 101 102 public void clearFlavorMaps() { 103 calculatedFlavorsMap.clear(); 104 selectedFlavorMap.clear(); 105 } 106 } 107 108 private final IItemFilter filter; 109 private final IItemCollection items; 110 private final IPageContainer pageContainer; 111 private final SelectionWithThreadAndRangeConsumer onSelectWithThreads; 112 113 private final Composite container; 114 // FIXME: We can remove all references to useSelectionButton if we decide that we are not going to use it 115 // private final Button useSelectionButton; 116 private final ComboViewer selectionCombo; 117 private final ComboViewer flavorCombo; 118 private final Canvas canvas; 119 private final RangePainter painter; 120 private Button showButton; 121 private Button resetButton; 122 private Button showConcurrentButton; 123 private Button rangeStyleButton; 124 private Button sameThreadsButton; 125 126 private boolean callbackActive = false; 127 private List<IAttribute<?>> attributes; 128 private FlavorSelectorState flavorSelectorState; 129 130 /** 131 * Creates a flavor selector. 132 * 133 * @param form 134 * Form to create selector in 135 * @param filter 136 * Filter to use when choosing flavors 137 * @param items 138 * Items to use when choosing flavors 139 * @param pageContainer 140 * Page container used for providing selection store and time range 141 * @param onSelect 142 * Called when a flavor is chosen. Arguments are the items from evaluating the flavor 143 * and the calculated time range for them. 144 * @return A flavor selector 145 */ 146 public static FlavorSelector itemsWithTimerange( 147 Form form, IItemFilter filter, IItemCollection items, IPageContainer pageContainer, 148 BiConsumer<IItemCollection, IRange<IQuantity>> onSelect, Consumer<Boolean> onShow, 149 FlavorSelectorState flavorSelectorState) { 150 return new FlavorSelector(form, filter, null, items, pageContainer, 151 (itemCollection, threadNames, range) -> onSelect.accept(itemCollection, range), 152 Optional.ofNullable(onShow), flavorSelectorState); 153 } 154 155 /** 156 * Creates a flavor selector. 157 * 158 * @param form 159 * Form to create selector in 160 * @param filter 161 * Filter to use when choosing flavors 162 * @param items 163 * Items to use when choosing flavors 164 * @param attributes 165 * Attributes to use when choosing flavors 166 * @param pageContainer 167 * Page container used for providing selection store and time range 168 * @param onSelectWithThreads 169 * Called when a flavor is chosen. Arguments are the items from evaluating the flavor 170 * (or null), the calculated thread names and time range 171 * @return A flavor selector 172 */ 173 public static FlavorSelector itemsWithTimerange( 174 Form form, IItemFilter filter, List<IAttribute<?>> attributes, IItemCollection items, 175 IPageContainer pageContainer, SelectionWithThreadAndRangeConsumer onSelectWithThreads, 176 FlavorSelectorState flavorSelectorState) { 177 return new FlavorSelector(form, filter, attributes, items, pageContainer, onSelectWithThreads, 178 Optional.ofNullable(null), flavorSelectorState); 179 } 180 181 /** 182 * Creates a flavor selector. 183 * 184 * @param form 185 * Form to create selector in 186 * @param filter 187 * Filter to use when choosing flavors 188 * @param attributes 189 * Attributes to use when choosing flavors 190 * @param items 191 * Items to use when choosing flavors 192 * @param pageContainer 193 * Page container used for providing selection store and time range 194 * @param onSelect 195 * Called when a flavor is chosen. Arguments are the items from evaluating the flavor 196 * (or null) and the calculated time range for them. 197 * @return A flavor selector 198 */ 199 public static FlavorSelector itemsWithTimerange( 200 Form form, IItemFilter filter, List<IAttribute<?>> attributes, IItemCollection items, 201 IPageContainer pageContainer, BiConsumer<IItemCollection, IRange<IQuantity>> onSelect, 202 FlavorSelectorState flavorSelectorState) { 203 return new FlavorSelector(form, filter, attributes, items, pageContainer, 204 (itemCollection, threadNames, range) -> onSelect.accept(itemCollection, range), 205 Optional.ofNullable(null), flavorSelectorState); 206 } 207 208 /** 209 * Creates a flavor selector. 210 * 211 * @param form 212 * Form to create selector in 213 * @param filter 214 * Filter to use when choosing flavors 215 * @param items 216 * Items to use when choosing flavors 217 * @param pageContainer 218 * Page container used for providing selection store and time range 219 * @param onSelect 220 * Called when a flavor is chosen. Arguments are the items from evaluating the flavor 221 * (or null) and the calculated time range for them. 222 * @return A flavor selector 223 */ 224 public static FlavorSelector itemsWithTimerange( 225 Form form, IItemFilter filter, IItemCollection items, IPageContainer pageContainer, 226 BiConsumer<IItemCollection, IRange<IQuantity>> onSelect, FlavorSelectorState flavorSelectorState) { 227 return new FlavorSelector(form, filter, null, items, pageContainer, 228 (itemCollection, threadNames, range) -> onSelect.accept(itemCollection, range), 229 Optional.ofNullable(null), flavorSelectorState); 230 } 231 232 private FlavorSelector(Form form, IItemFilter filter, List<IAttribute<?>> attributes, IItemCollection items, 233 IPageContainer pageContainer, SelectionWithThreadAndRangeConsumer onSelectWithThreads, 234 Optional<Consumer<Boolean>> onShow, FlavorSelectorState flavorSelectorState) { 235 this.filter = filter; 236 this.attributes = attributes; 237 this.items = items; 238 this.pageContainer = pageContainer; 239 this.onSelectWithThreads = onSelectWithThreads; 240 flavorSelectorState = flavorSelectorState != null ? flavorSelectorState : new FlavorSelectorState(); 241 this.flavorSelectorState = flavorSelectorState; 242 243 container = new Composite(form.getHead(), SWT.NONE); 244 container.setLayout(GridLayoutFactory.fillDefaults().margins(0, 0).spacing(2, 2).create()); 245 246 Composite selectorRow = new Composite(container, SWT.NONE); 247 selectorRow 248 .setLayoutData(GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).grab(true, false).create()); 249 selectorRow.setLayout(GridLayoutFactory.swtDefaults().numColumns(9).create()); 250 251 // useSelectionButton = new Button(selectorRow, SWT.CHECK); 252 // useSelectionButton.setLayoutData(GridDataFactory.swtDefaults().create()); 253 // useSelectionButton.setText("Filter on:"); 254 // useSelectionButton.setEnabled(pageContainer.getSelectionStore().getSelections().count() > 0); 255 // useSelectionButton.setSelection(pageContainer.getSelectionStore().isCurrentActive()); 256 // useSelectionButton.addSelectionListener(new SelectionCheckboxSelectionListener()); 257 258 selectionCombo = new ComboViewer(selectorRow); 259 selectionCombo.getCombo().setLayoutData(GridDataFactory.swtDefaults().hint(200, SWT.DEFAULT) 260 .minSize(100, SWT.DEFAULT).grab(false, false).create()); 261 selectionCombo.setContentProvider(new SelectionComboContentProvider()); 262 selectionCombo.setLabelProvider(new SelectionComboLabelProvider()); 263 selectionCombo.addSelectionChangedListener(new SelectionComboSelectionListener()); 264 // selectionCombo.getControl().setEnabled(pageContainer.getSelectionStore().isCurrentActive()); 265 266 Label flavorLabel = new Label(selectorRow, SWT.NONE); 267 flavorLabel.setLayoutData(GridDataFactory.swtDefaults().create()); 268 flavorLabel.setText(Messages.FlavorSelector_LABEL_ASPECT); 269 270 flavorCombo = new ComboViewer(selectorRow); 271 flavorCombo.getCombo().setLayoutData(GridDataFactory.swtDefaults().hint(300, SWT.DEFAULT) 272 .minSize(100, SWT.DEFAULT).grab(true, false).create()); 273 flavorCombo.setContentProvider(new FlavorComboContentProvider()); 274 flavorCombo.setLabelProvider(new FlavorComboLabelProvider()); 275 flavorCombo.addSelectionChangedListener(new FlavorComboSelectionListener()); 276 // flavorCombo.getControl().setEnabled(pageContainer.getSelectionStore().isCurrentActive()); 277 278 showConcurrentButton = new Button(selectorRow, SWT.CHECK); 279 showConcurrentButton.setLayoutData(GridDataFactory.swtDefaults().create()); 280 showConcurrentButton.setText(Messages.FlavorSelector_BUTTON_SHOW_CONCURRENT); 281 showConcurrentButton.setToolTipText(Messages.FlavorSelector_BUTTON_SHOW_CONCURRENT_TOOLTIP); 282 showConcurrentButton.setSelection(this.flavorSelectorState.showConcurrent); 283 showConcurrentButton.addSelectionListener(new ShowConcurrentSelectionListener()); 284 285 // FIXME: Instead use radio buttons with images? 286 rangeStyleButton = new Button(selectorRow, SWT.CHECK); 287 rangeStyleButton.setLayoutData(GridDataFactory.swtDefaults().create()); 288 rangeStyleButton.setText(Messages.FlavorSelector_BUTTON_CONTAINED); 289 rangeStyleButton.setToolTipText(Messages.FlavorSelector_BUTTON_CONTAINED_TOOLTIP); 290 rangeStyleButton.setEnabled(showConcurrentButton.getSelection()); 291 rangeStyleButton.setSelection(flavorSelectorState.concurrentContained); 292 rangeStyleButton.addSelectionListener(new RangeStyleSelectionListener()); 293 294 // FIXME: Instead use radio buttons with images? 295 sameThreadsButton = new Button(selectorRow, SWT.CHECK); 296 sameThreadsButton.setLayoutData(GridDataFactory.swtDefaults().create()); 297 sameThreadsButton.setText(Messages.FlavorSelector_BUTTON_SAME_THREADS); 298 sameThreadsButton.setToolTipText(Messages.FlavorSelector_BUTTON_SAME_THREADS_TOOLTIP); 299 sameThreadsButton.setEnabled(showConcurrentButton.getSelection()); 300 sameThreadsButton.setSelection(flavorSelectorState.sameThreads); 301 sameThreadsButton.addSelectionListener(new SameThreadsSelectionListener()); 302 303 // FIXME: Persist state for above checkboxes? 304 305 onShow.ifPresent(on -> { 306 Label rangeLabel = new Label(selectorRow, SWT.NONE); 307 rangeLabel.setLayoutData(GridDataFactory.swtDefaults().create()); 308 rangeLabel.setText(Messages.FlavorSelector_LABEL_TIMERANGE); 309 showButton = new Button(selectorRow, SWT.PUSH); 310 showButton.setText(Messages.FlavorSelector_BUTTON_TIMERANGE_SET); 311 showButton.setToolTipText(Messages.FlavorSelector_BUTTON_TIMERANGE_SET_TOOLTIP); 312 showButton.setLayoutData(GridDataFactory.swtDefaults().create()); 313 resetButton = new Button(selectorRow, SWT.PUSH); 314 resetButton.setText(Messages.FlavorSelector_BUTTON_TIMERANGE_CLEAR); 315 resetButton.setToolTipText(Messages.FlavorSelector_BUTTON_TIMERANGE_CLEAR_TOOLTIP); 316 resetButton.addListener(SWT.Selection, e -> on.accept(false)); 317 resetButton.setLayoutData(GridDataFactory.swtDefaults().create()); 318 showButton.addListener(SWT.Selection, e -> on.accept(true)); 319 }); 320 321 canvas = new Canvas(container, SWT.NO_BACKGROUND); 322 canvas.setLayoutData(GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).hint(SWT.DEFAULT, 7) 323 .grab(true, false).create()); 324 painter = new RangePainter(canvas, pageContainer.getRecordingRange()); 325 326 selectionCombo.setInput(pageContainer.getSelectionStore()); 327 selectionCombo.setSelection(getCurrentSelection()); 328 callbackActive = true; 329 330 enableSelection(); 331 332 IItemStreamFlavor currentFlavor = null; 333 if (pageContainer.getSelectionStore().isCurrentActive()) { 334 currentFlavor = getSelectedFlavor(); 335 } 336 useFlavor(currentFlavor); 337 338 pageContainer.getSelectionStore().setListener(this); 339 340 form.setHeadClient(container); 341 } 342 343 private ISelection getCurrentSelection() { 344 return pageContainer.getSelectionStore().getCurrentSelection() != null 345 ? new StructuredSelection(pageContainer.getSelectionStore().getCurrentSelection()) 346 : new StructuredSelection(selectionCombo.getElementAt(0)); 347 } 348 349 @Override 350 public void selectionActive(boolean active) { 351 // useSelectionButton.setSelection(active); 352 selectionCombo.setSelection(getCurrentSelection()); 353 } 354 355 @Override 356 public void selectionAdded(SelectionStoreEntry selection) { 357 // useSelectionButton.setEnabled(true); 358 if (!selectionCombo.getControl().isDisposed()) { 359 selectionCombo.refresh(); 360 } 361 } 362 363 private static String formatRange(IRange<IQuantity> range) { 364 return range.getStart().displayUsing(IDisplayable.AUTO) + " - ( " //$NON-NLS-1$ 365 + range.getExtent().displayUsing(IDisplayable.AUTO) + " ) - " //$NON-NLS-1$ 366 + range.getEnd().displayUsing(IDisplayable.AUTO); 367 } 368 369 public void enableSelection() { 370 boolean enabled = true; 371 pageContainer.getSelectionStore().setCurrentActive(enabled); 372 selectionCombo.getCombo().setEnabled(enabled); 373 flavorCombo.getCombo().setEnabled(enabled); 374 // FIXME: Make sure not to call useFlavor twice during initialization. 375 // IItemStreamFlavor flavor = null; 376 // if (enabled) { 377 // flavor = getSelectedFlavor(); 378 // } 379 // useFlavor(flavor); 380 } 381 382 public FlavorSelectorState getFlavorSelectorState() { 383 return flavorSelectorState; 384 } 385 386 private IItemStreamFlavor getSelectedFlavor() { 387 IItemStreamFlavor flavor = null; 388 ISelection s = flavorCombo.getSelection(); 389 if (s instanceof IStructuredSelection) { 390 Object obj = ((IStructuredSelection) s).getFirstElement(); 391 if (obj instanceof IItemStreamFlavor) { 392 flavor = (IItemStreamFlavor) obj; 393 } 394 } 395 return flavor; 396 } 397 398 private static final class SelectionComboContentProvider implements IStructuredContentProvider { 399 private SelectionStore store; 400 401 @Override 402 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { 403 if (newInput instanceof SelectionStore) { 404 store = (SelectionStore) newInput; 405 } 406 } 407 408 @Override 409 public void dispose() { 410 } 411 412 @Override 413 public Object[] getElements(Object inputElement) { 414 // FIXME: This is for if we enable/disable selection usage outside of the combo 415 // if (store.getSelections().count() == 0) { 416 // return new String[] { "<No selection stored>" }; 417 // } 418 return store.getSelections().toArray(); 419 } 420 } 421 422 public interface SelectionWithThreadAndRangeConsumer { 423 public void accept(IItemCollection items, Set<String> threadNames, IRange<IQuantity> range); 424 } 425 426 private static final class SelectionComboLabelProvider extends LabelProvider { 427 @Override 428 public String getText(Object element) { 429 if (element instanceof SelectionStoreEntry) { 430 SelectionStoreEntry entry = (SelectionStoreEntry) element; 431 return entry.getName(); 432 } 433 return super.getText(element); 434 } 435 } 436 437 private final class SelectionComboSelectionListener implements ISelectionChangedListener { 438 @Override 439 public void selectionChanged(SelectionChangedEvent event) { 440 flavorCombo.getCombo().removeAll(); 441 if (event.getSelection() instanceof IStructuredSelection) { 442 Object obj = ((IStructuredSelection) (event.getSelection())).getFirstElement(); 443 if (obj instanceof SelectionStoreEntry) { 444 SelectionStoreEntry entry = (SelectionStoreEntry) obj; 445 IFlavoredSelection selection = entry.getSelection(); 446 pageContainer.getSelectionStore().setCurrent(selection); 447 448 IItemStreamFlavor[] flavors = flavorSelectorState.calculatedFlavorsMap.get(selection); 449 if (flavors == null) { 450 flavors = selection.getFlavors(filter, items, attributes).toArray(IItemStreamFlavor[]::new); 451 flavorSelectorState.calculatedFlavorsMap.put(selection, flavors); 452 } 453 flavorCombo.setInput(flavors); 454 455 IItemStreamFlavor selectedFlavor = flavorSelectorState.selectedFlavorMap.get(selection); 456 if (selectedFlavor == null) { 457 if (flavors.length > 0) { 458 selectedFlavor = flavors[0]; 459 flavorSelectorState.selectedFlavorMap.put(selection, selectedFlavor); 460 } 461 } 462 if (selectedFlavor != null) { 463 flavorCombo.setSelection(new StructuredSelection(selectedFlavor)); 464 } 465 466 trimFlavorMaps(); 467 } 468 } 469 } 470 } 471 472 private static final class FlavorComboContentProvider implements IStructuredContentProvider { 473 private IItemStreamFlavor[] flavors; 474 475 @Override 476 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { 477 if (newInput instanceof IItemStreamFlavor[]) { 478 flavors = (IItemStreamFlavor[]) newInput; 479 } 480 } 481 482 @Override 483 public void dispose() { 484 485 } 486 487 @Override 488 public Object[] getElements(Object inputElement) { 489 if (flavors == null || flavors.length == 0) { 490 return new String[] {"<" + Messages.FlavorSelector_LABEL_NO_SELECTION + ">"}; //$NON-NLS-1$ //$NON-NLS-2$ 491 } 492 return flavors; 493 } 494 } 495 496 private static final class FlavorComboLabelProvider extends LabelProvider { 497 @Override 498 public String getText(Object element) { 499 if (element instanceof IItemStreamFlavor) { 500 IItemStreamFlavor sel = (IItemStreamFlavor) element; 501 return sel.getName(); 502 } 503 return super.getText(element); 504 } 505 } 506 507 private final class FlavorComboSelectionListener implements ISelectionChangedListener { 508 @Override 509 public void selectionChanged(SelectionChangedEvent event) { 510 IItemStreamFlavor flavor = null; 511 if (event.getSelection() instanceof IStructuredSelection) { 512 Object obj = ((IStructuredSelection) event.getSelection()).getFirstElement(); 513 if (obj instanceof IItemStreamFlavor) { 514 flavor = ((IItemStreamFlavor) obj); 515 SelectionStoreEntry entry = pageContainer.getSelectionStore().getCurrentSelection(); 516 if (entry != null) { 517 flavorSelectorState.selectedFlavorMap.put(entry.getSelection(), flavor); 518 } 519 } 520 } 521 useFlavor(flavor); 522 } 523 } 524 525 private void useFlavor(IItemStreamFlavor flavor) { 526 if (callbackActive) { 527 Optional<IRange<IQuantity>> range = FlavorToolkit.getRange(flavor); 528 painter.current = range.orElse(null); 529 canvas.setVisible(painter.current != null); 530 canvas.setToolTipText(range.map(FlavorSelector::formatRange).orElse(null)); 531 container.layout(); 532 533 // FIXME: Always use concurrent if (all?) items can't be displayed on page? 534 IItemCollection itemsToUse = null; 535 Set<IMCThread> threads = FlavorToolkit.getThreads(getSelectedFlavor(), flavorSelectorState.showConcurrent, 536 flavorSelectorState.sameThreads); 537 if (flavor != null && !flavor.isEmpty()) { 538 IItemFilter rangeAndThreadFilter = FlavorToolkit.getRangeAndThreadFilter(range, threads, 539 flavorSelectorState.showConcurrent, flavorSelectorState.concurrentContained, 540 flavorSelectorState.sameThreads); 541 if (rangeAndThreadFilter != null) { 542 itemsToUse = items.apply(rangeAndThreadFilter); 543 } else { 544 itemsToUse = flavor.evaluate(); 545 } 546 } 547 Set<String> threadNames = FlavorToolkit.getThreadNames(threads, flavor); 548 onSelectWithThreads.accept(itemsToUse, threadNames, range.orElse(pageContainer.getRecordingRange())); 549 } 550 } 551 552 public void trimFlavorMaps() { 553 // NOTE: It should be enough to keep the map sizes below 2 * storesize 554 if (flavorSelectorState.calculatedFlavorsMap 555 .size() > (2 * pageContainer.getSelectionStore().getSelections().count())) { 556 557 List<IFlavoredSelection> storedSelections = pageContainer.getSelectionStore().getSelections() 558 .map(sse -> sse.getSelection()).collect(Collectors.toList()); 559 560 for (Iterator<Entry<IFlavoredSelection, IItemStreamFlavor[]>> iterator = flavorSelectorState.calculatedFlavorsMap 561 .entrySet().iterator(); iterator.hasNext();) { 562 IFlavoredSelection selection = iterator.next().getKey(); 563 if (!storedSelections.contains(selection)) { 564 iterator.remove(); 565 } 566 } 567 568 for (Iterator<Entry<IFlavoredSelection, IItemStreamFlavor>> iterator = flavorSelectorState.selectedFlavorMap 569 .entrySet().iterator(); iterator.hasNext();) { 570 IFlavoredSelection selection = iterator.next().getKey(); 571 if (!storedSelections.contains(selection)) { 572 iterator.remove(); 573 } 574 } 575 } 576 } 577 578 public class ShowConcurrentSelectionListener extends SelectionAdapter { 579 @Override 580 public void widgetSelected(SelectionEvent e) { 581 flavorSelectorState.showConcurrent = showConcurrentButton.getSelection(); 582 rangeStyleButton.setEnabled(flavorSelectorState.showConcurrent); 583 sameThreadsButton.setEnabled(flavorSelectorState.showConcurrent); 584 useFlavor(getSelectedFlavor()); 585 } 586 } 587 588 public class RangeStyleSelectionListener extends SelectionAdapter { 589 @Override 590 public void widgetSelected(SelectionEvent e) { 591 flavorSelectorState.concurrentContained = rangeStyleButton.getSelection(); 592 useFlavor(getSelectedFlavor()); 593 } 594 } 595 596 public class SameThreadsSelectionListener extends SelectionAdapter { 597 @Override 598 public void widgetSelected(SelectionEvent e) { 599 flavorSelectorState.sameThreads = sameThreadsButton.getSelection(); 600 useFlavor(getSelectedFlavor()); 601 } 602 } 603 604 private static class RangePainter implements PaintListener { 605 606 private final Control canvas; 607 private final IQuantity start; 608 private final IQuantity end; 609 610 IRange<IQuantity> current; 611 612 RangePainter(Control canvas, IRange<IQuantity> fullRange) { 613 this.canvas = canvas; 614 start = fullRange.getStart(); 615 end = fullRange.getEnd(); 616 canvas.addPaintListener(this); 617 } 618 619 @Override 620 public void paintControl(PaintEvent e) { 621 if (current != null) { 622 Point size = canvas.getSize(); 623 624 e.gc.setBackground(SWTColorToolkit.getColor(new RGB(200, 200, 200))); 625 e.gc.setForeground(SWTColorToolkit.getColor(new RGB(120, 120, 120))); 626 e.gc.fillRectangle(0, 0, size.x, size.y); 627 e.gc.drawRectangle(0, 0, size.x - 1, size.y - 1); 628 629 SubdividedQuantityRange fullRangeAxis = new SubdividedQuantityRange(start, end, size.x, 25); 630 int x1 = (int) fullRangeAxis.getPixel(current.getStart()); 631 int x2 = (int) Math.ceil(fullRangeAxis.getPixel(current.getEnd())); 632 e.gc.setForeground(SWTColorToolkit.getColor(new RGB(221, 58, 22))); 633 e.gc.setBackground(SWTColorToolkit.getColor(new RGB(252, 128, 3))); 634 e.gc.fillGradientRectangle(x1, 0, x2 - x1, size.y, true); 635 e.gc.setForeground(SWTColorToolkit.getColor(new RGB(0, 0, 0))); 636 e.gc.drawRectangle(x1, 0, x2 - x1 - 1, size.y - 1); 637 } 638 } 639 } 640 }