1 /*******************************************************************************
2 * Copyright (c) 2016 EfficiOS Inc., Jonathan Rajotte-Julien
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
8 *******************************************************************************/
10 package org
.eclipse
.tracecompass
.internal
.provisional
.analysis
.lami
.ui
.views
;
12 import static org
.eclipse
.tracecompass
.common
.core
.NonNullUtils
.checkNotNull
;
14 import java
.util
.ArrayList
;
15 import java
.util
.Arrays
;
16 import java
.util
.List
;
17 import java
.util
.function
.Function
;
18 import java
.util
.function
.Predicate
;
19 import java
.util
.stream
.IntStream
;
21 import org
.eclipse
.jdt
.annotation
.Nullable
;
22 import org
.eclipse
.jface
.dialogs
.Dialog
;
23 import org
.eclipse
.jface
.dialogs
.IDialogConstants
;
24 import org
.eclipse
.jface
.layout
.TableColumnLayout
;
25 import org
.eclipse
.jface
.viewers
.ArrayContentProvider
;
26 import org
.eclipse
.jface
.viewers
.CheckboxTableViewer
;
27 import org
.eclipse
.jface
.viewers
.ColumnLabelProvider
;
28 import org
.eclipse
.jface
.viewers
.ColumnWeightData
;
29 import org
.eclipse
.jface
.viewers
.ILabelProvider
;
30 import org
.eclipse
.jface
.viewers
.IStructuredContentProvider
;
31 import org
.eclipse
.jface
.viewers
.IStructuredSelection
;
32 import org
.eclipse
.jface
.viewers
.TableViewer
;
33 import org
.eclipse
.jface
.viewers
.TableViewerColumn
;
34 import org
.eclipse
.swt
.SWT
;
35 import org
.eclipse
.swt
.custom
.SashForm
;
36 import org
.eclipse
.swt
.events
.SelectionEvent
;
37 import org
.eclipse
.swt
.events
.SelectionListener
;
38 import org
.eclipse
.swt
.layout
.GridData
;
39 import org
.eclipse
.swt
.layout
.GridLayout
;
40 import org
.eclipse
.swt
.widgets
.Button
;
41 import org
.eclipse
.swt
.widgets
.Composite
;
42 import org
.eclipse
.swt
.widgets
.Control
;
43 import org
.eclipse
.swt
.widgets
.Display
;
44 import org
.eclipse
.swt
.widgets
.Group
;
45 import org
.eclipse
.swt
.widgets
.Label
;
46 import org
.eclipse
.swt
.widgets
.Shell
;
47 import org
.eclipse
.swt
.widgets
.TableColumn
;
48 import org
.eclipse
.swt
.widgets
.TableItem
;
49 import org
.eclipse
.tracecompass
.internal
.provisional
.analysis
.lami
.core
.aspect
.LamiTableEntryAspect
;
50 import org
.eclipse
.tracecompass
.internal
.provisional
.analysis
.lami
.core
.module
.LamiChartModel
.ChartType
;
51 import org
.eclipse
.tracecompass
.internal
.provisional
.analysis
.lami
.core
.module
.LamiXYSeriesDescription
;
52 import org
.eclipse
.ui
.dialogs
.SelectionDialog
;
55 * Series creation dialog
57 * @author Jonathan Rajotte-Julien
59 public class LamiSeriesDialog
extends SelectionDialog
{
61 private static final int MINIMUM_COLUMN_WIDTH
= 30;
62 private static final int MININAL_SERIES_TABLE_HEIGHT
= 150;
64 /* The root element to populate the viewer with */
65 private final Object fXInputElement
;
66 private final Object fYInputElement
;
67 private final List
<LamiXYSeriesDescription
> series
;
69 /* Providers for populating dialog */
70 private final ILabelProvider fXLabelProvider
;
71 private final IStructuredContentProvider fXContentProvider
;
72 private final ILabelProvider fYLabelProvider
;
73 private final IStructuredContentProvider fYContentProvider
;
74 private final IStructuredContentProvider fSeriesContentProvider
;
76 private final boolean fRestrictXSeriesNumbers
;
78 private final List
<LamiAxisCheckBoxOption
> fXCheckBoxOptions
;
79 private final List
<LamiAxisCheckBoxOption
> fYCheckBoxOptions
;
81 // the visual selection widget group
82 private TableViewer fXTableViewer
;
83 private CheckboxTableViewer fYCheckBoxViewer
;
84 private TableViewer fSeriesListViewer
;
86 private Label fWarning
;
90 * The parent shell of the dialog
92 * The chart type for which the dialog construct series
94 * The possible X axis set of values
96 * The possible Y axis set of values
97 * @param xContentProvider
98 * A content provider for the X axis set
99 * @param xLabelProvider
100 * The label provider for the X axis set
101 * @param yContentProvider
102 * The content provider for the Y axis set
103 * @param yLabelProvider
104 * The label provider for the Y axis set
106 public LamiSeriesDialog(Shell parentShell
, ChartType chartType
, Object xInput
,
108 IStructuredContentProvider xContentProvider
,
109 ILabelProvider xLabelProvider
,
110 IStructuredContentProvider yContentProvider
,
111 ILabelProvider yLabelProvider
) {
113 fXInputElement
= xInput
;
114 fYInputElement
= yInput
;
115 fXContentProvider
= xContentProvider
;
116 fXLabelProvider
= xLabelProvider
;
117 fYContentProvider
= yContentProvider
;
118 fYLabelProvider
= yLabelProvider
;
119 series
= new ArrayList
<>();
120 fSeriesContentProvider
= checkNotNull(ArrayContentProvider
.getInstance());
122 fXCheckBoxOptions
= new ArrayList
<>();
123 fYCheckBoxOptions
= new ArrayList
<>();
124 fSeriesListViewer
= new TableViewer(parentShell
);
125 fXTableViewer
= new TableViewer(parentShell
);
126 fYCheckBoxViewer
= checkNotNull(CheckboxTableViewer
.newCheckList(parentShell
, SWT
.NONE
));
128 /* Dynamic restriction per chart type */
131 fRestrictXSeriesNumbers
= false;
136 fRestrictXSeriesNumbers
= true;
140 this.fWarning
= new Label(parentShell
, SWT
.NONE
);
144 protected Control
createDialogArea(@Nullable Composite parent
) {
146 Composite composite
= (Composite
) super.createDialogArea(parent
);
147 initializeDialogUnits(composite
);
149 /* Base 3 column grid layout */
150 GridLayout gridLayout
= new GridLayout(3, false);
151 composite
.setLayout(gridLayout
);
153 GridData gridData
= new GridData(GridData
.FILL_BOTH
);
154 gridData
.horizontalSpan
= 3;
155 Group seriesGroup
= new Group(composite
, SWT
.NONE
);
156 seriesGroup
.setLayoutData(gridData
);
157 seriesGroup
.setLayout(new GridLayout(3, false));
158 seriesGroup
.setText(Messages
.LamiSeriesDialog_series
);
161 * New sub group for the series table.
163 gridData
= new GridData(GridData
.FILL_BOTH
);
164 gridData
.horizontalSpan
= 2;
165 gridData
.heightHint
= MININAL_SERIES_TABLE_HEIGHT
;
166 Group seriesTableGroup
= new Group(seriesGroup
, SWT
.NONE
);
167 seriesTableGroup
.setLayoutData(gridData
);
168 TableColumnLayout layout
= new TableColumnLayout();
169 seriesTableGroup
.setLayout(layout
);
172 fSeriesListViewer
= new TableViewer(seriesTableGroup
, SWT
.MULTI
| SWT
.H_SCROLL
| SWT
.V_SCROLL
| SWT
.BORDER
);
173 fSeriesListViewer
.setContentProvider(fSeriesContentProvider
);
174 fSeriesListViewer
.setInput(series
);
175 fSeriesListViewer
.getTable().setHeaderVisible(true);
176 fSeriesListViewer
.getTable().setLinesVisible(true);
177 TableViewerColumn column1
= createTableViewerColumn(fSeriesListViewer
, Messages
.LamiSeriesDialog_x_values
, element
-> element
.getXAspect().getLabel());
178 TableViewerColumn column2
= createTableViewerColumn(fSeriesListViewer
, Messages
.LamiSeriesDialog_y_values
, element
-> element
.getYAspect().getLabel());
179 layout
.setColumnData(column1
.getColumn(), new ColumnWeightData(1, MINIMUM_COLUMN_WIDTH
, true));
180 layout
.setColumnData(column2
.getColumn(), new ColumnWeightData(1, MINIMUM_COLUMN_WIDTH
, true));
182 /* Delete series button */
183 gridData
= new GridData(GridData
.CENTER
);
184 gridData
.horizontalSpan
= 1;
185 Button deleteSeries
= new Button(seriesGroup
, SWT
.PUSH
);
186 deleteSeries
.setText(Messages
.LamiSeriesDialog_delete
);
187 deleteSeries
.setLayoutData(gridData
);
188 deleteSeries
.addSelectionListener(new SelectionListener() {
190 public void widgetSelected(@Nullable SelectionEvent e
) {
191 /* Remove the selectecd series */
192 IStructuredSelection selections
= (IStructuredSelection
) fSeriesListViewer
.getSelection();
193 for (Object selection
: selections
.toList()) {
194 series
.remove(selection
);
196 /* When table is empty reset to initial state */
197 if (series
.isEmpty()) {
198 /* Make sure the OK button is disabled */
199 getButton(IDialogConstants
.OK_ID
).setEnabled(false);
200 /* Hide the selection warning */
201 fWarning
.setVisible(false);
204 * Reset the initial selection of the X axis selection table
206 fXTableViewer
.refresh();
207 /* Reset check boxes options */
208 fXCheckBoxOptions
.forEach(checkBox
-> {
209 checkBox
.setButtonEnabled(true);
211 fYCheckBoxOptions
.forEach(checkBox
-> {
212 checkBox
.setButtonEnabled(true);
215 /* Refresh the series table to show the added series */
216 fSeriesListViewer
.refresh();
220 public void widgetDefaultSelected(@Nullable SelectionEvent e
) {
225 * Series creator subgroup
227 gridData
= new GridData(GridData
.FILL_BOTH
);
228 gridData
.horizontalSpan
= 3;
229 Group seriesCreatorGroup
= new Group(composite
, getShellStyle());
230 seriesCreatorGroup
.setLayoutData(gridData
);
231 seriesCreatorGroup
.setLayout(new GridLayout(3, false));
232 seriesCreatorGroup
.setText(Messages
.LamiSeriesDialog_serie_creator
);
234 /* X axis sash label */
235 gridData
= new GridData(GridData
.FILL_BOTH
| GridData
.VERTICAL_ALIGN_END
);
236 gridData
.horizontalSpan
= 1;
237 Label xSeriesCreatorLabel
= new Label(seriesCreatorGroup
, SWT
.CENTER
);
238 xSeriesCreatorLabel
.setLayoutData(gridData
);
239 xSeriesCreatorLabel
.setText(Messages
.LamiSeriesDialog_x_axis
);
241 gridData
= new GridData(GridData
.FILL_BOTH
| GridData
.VERTICAL_ALIGN_END
);
242 gridData
.horizontalSpan
= 1;
243 Label ySeriesCreatorLabel
= new Label(seriesCreatorGroup
, SWT
.CENTER
);
244 ySeriesCreatorLabel
.setLayoutData(gridData
);
245 ySeriesCreatorLabel
.setText(Messages
.LamiSeriesDialog_y_axis
);
247 /* Empty label for grid layout */
248 gridData
= new GridData(GridData
.FILL_BOTH
);
249 gridData
.horizontalSpan
= 1;
250 Label emptyLabel
= new Label(seriesCreatorGroup
, SWT
.CENTER
);
251 emptyLabel
.setLayoutData(gridData
);
253 SashForm sash1
= new SashForm(seriesCreatorGroup
, SWT
.BORDER
| SWT
.HORIZONTAL
);
254 gridData
= new GridData(GridData
.FILL_BOTH
);
255 gridData
.horizontalSpan
= 2;
256 sash1
.setLayoutData(gridData
);
257 sash1
.setVisible(true);
259 fXTableViewer
= new TableViewer(sash1
, getTableStyle());
260 fXTableViewer
.setContentProvider(fXContentProvider
);
261 fXTableViewer
.setLabelProvider(fXLabelProvider
);
262 fXTableViewer
.setInput(fXInputElement
);
264 fYCheckBoxViewer
= checkNotNull(CheckboxTableViewer
.newCheckList(sash1
, SWT
.BORDER
));
265 fYCheckBoxViewer
.setLabelProvider(fYLabelProvider
);
266 fYCheckBoxViewer
.setContentProvider(fYContentProvider
);
267 fYCheckBoxViewer
.setInput(fYInputElement
);
269 gridData
= new GridData(SWT
.FILL
, SWT
.NONE
, true, true);
270 gridData
.horizontalSpan
= 1;
271 Button button1
= new Button(seriesCreatorGroup
, SWT
.PUSH
);
272 button1
.setText(Messages
.LamiSeriesDialog_add
);
273 button1
.setLayoutData(gridData
);
274 button1
.addSelectionListener(new SelectionListener() {
277 public void widgetSelected(@Nullable SelectionEvent e
) {
278 Object
[] ySelections
= fYCheckBoxViewer
.getCheckedElements();
279 IStructuredSelection xSelections
= (IStructuredSelection
) fXTableViewer
.getSelection();
280 @Nullable Object x
= xSelections
.getFirstElement();
281 if (!(x
instanceof LamiTableEntryAspect
) || ySelections
.length
== 0) {
285 /* Add selection to series if it doesn not already exist in the list */
286 for (Object y
: ySelections
) {
287 if(!(y
instanceof LamiTableEntryAspect
)) {
290 LamiXYSeriesDescription serie
= new LamiXYSeriesDescription((LamiTableEntryAspect
) x
, ((LamiTableEntryAspect
) y
));
291 if (!series
.contains(serie
)) {
293 fSeriesListViewer
.refresh();
297 /* Set label warning visible and enable OK button */
298 fWarning
.setVisible(true);
299 getButton(IDialogConstants
.OK_ID
).setEnabled(true);
301 /* Update possible X selection based on current series */
302 TableItem
[] items
= fXTableViewer
.getTable().getItems();
303 Arrays
.stream(items
).forEach(item
-> {
304 LamiTableEntryAspect aspect
= (LamiTableEntryAspect
) item
.getData();
305 if (!aspect
.arePropertiesEqual(series
.get(0).getXAspect())) {
306 fXTableViewer
.remove(aspect
);
308 if (fRestrictXSeriesNumbers
&& aspect
!= (series
.get(0).getXAspect())) {
309 fXTableViewer
.remove(aspect
);
314 * Disable all checkBox that do not apply to aspects series.
315 * Simply take the first one since all series should comply to
316 * the same restriction
318 fXCheckBoxOptions
.forEach(checkBox
-> {
319 checkBox
.setButtonEnabled(checkBox
.getPredicate().test(series
.get(0).getXAspect()));
321 fYCheckBoxOptions
.forEach(checkBox
-> {
322 checkBox
.setButtonEnabled(checkBox
.getPredicate().test(series
.get(0).getYAspect()));
327 public void widgetDefaultSelected(@Nullable SelectionEvent e
) {
332 gridData
= new GridData(GridData
.FILL_BOTH
| GridData
.VERTICAL_ALIGN_END
);
333 gridData
.horizontalSpan
= 3;
334 fWarning
= new Label(seriesCreatorGroup
, SWT
.LEFT
);
335 fWarning
.setLayoutData(gridData
);
336 fWarning
.setText(Messages
.LamiSeriesDialog_selectionRestrictionWarning
);
337 fWarning
.setForeground(Display
.getCurrent().getSystemColor(SWT
.COLOR_RED
));
338 fWarning
.setVisible(false);
340 gridData
= new GridData(GridData
.FILL_BOTH
);
341 gridData
.horizontalSpan
= 3;
342 Group optionGroups
= new Group(composite
, getShellStyle());
343 optionGroups
.setLayoutData(gridData
);
344 optionGroups
.setLayout(new GridLayout(3, false));
345 optionGroups
.setText(Messages
.LamiSeriesDialog_chart_options
);
347 for (LamiAxisCheckBoxOption checkBox
: fXCheckBoxOptions
) {
348 Button button
= new Button(optionGroups
, SWT
.CHECK
);
349 button
.setSelection(checkBox
.getDefaultValue());
350 button
.setText(checkBox
.getName());
351 checkBox
.setButton(button
);
354 for (LamiAxisCheckBoxOption checkBox
: fYCheckBoxOptions
) {
355 Button button
= new Button(optionGroups
, SWT
.CHECK
);
356 button
.setSelection(checkBox
.getDefaultValue());
357 button
.setText(checkBox
.getName());
358 checkBox
.setButton(button
);
361 fYCheckBoxViewer
.getTable().addSelectionListener(new SelectionListener() {
364 public void widgetSelected(@Nullable SelectionEvent e
) {
366 if (e
!= null && e
.detail
== SWT
.CHECK
) {
367 /* Change possible selection */
368 IStructuredSelection selections
= (IStructuredSelection
) fYCheckBoxViewer
.getSelection();
369 if (selections
.getFirstElement() != null) {
371 boolean checked
= fYCheckBoxViewer
.getChecked(selections
.getFirstElement());
373 * If just selected look for stuff to disable. If not no
374 * need to look for stuff to disable since it was
375 * already done before.
378 TableItem
[] items
= fYCheckBoxViewer
.getTable().getItems();
379 Arrays
.stream(items
).forEach(item
-> {
380 LamiTableEntryAspect aspect
= (LamiTableEntryAspect
) item
.getData();
381 if (!aspect
.arePropertiesEqual((LamiTableEntryAspect
) checkNotNull(selections
.getFirstElement()))) {
382 fYCheckBoxViewer
.remove(aspect
);
385 } else if (!checked
&& fYCheckBoxViewer
.getCheckedElements().length
== 0 && fSeriesListViewer
.getTable().getItemCount() == 0) {
386 fYCheckBoxViewer
.refresh();
393 public void widgetDefaultSelected(@Nullable SelectionEvent e
) {
397 Dialog
.applyDialogFont(composite
);
402 * Disable OK button on dialog creation.
405 protected void createButtonsForButtonBar(@Nullable Composite parent
) {
406 super.createButtonsForButtonBar(parent
);
407 getButton(IDialogConstants
.OK_ID
).setEnabled(false);
411 * Return the style flags for the table viewer.
415 protected int getTableStyle() {
416 return SWT
.SINGLE
| SWT
.H_SCROLL
| SWT
.V_SCROLL
| SWT
.BORDER
;
420 * Add check box option for X series.
423 * The name of the option. The actual text shown to the user.
424 * @param defaultValue
425 * The default state of the check box option.
427 * The predicate to check if the option applies to the given
429 * @return The index of the option value in the result table.
431 public int addXCheckBoxOption(String name
, boolean defaultValue
, Predicate
<LamiTableEntryAspect
> predicate
) {
432 LamiAxisCheckBoxOption checkBox
= new LamiAxisCheckBoxOption(name
, defaultValue
, predicate
);
433 fXCheckBoxOptions
.add(checkBox
);
434 return fXCheckBoxOptions
.size() - 1;
438 * Add check box option for Y series.
441 * The name of the option. The actual text shown to the user.
442 * @param defaultValue
443 * The default state of the check box option.
445 * The predicate to check if the option applies to the given
447 * @return The index of the option value in the result table.
449 public int addYCheckBoxOption(String name
, boolean defaultValue
, Predicate
<LamiTableEntryAspect
> predicate
) {
450 LamiAxisCheckBoxOption checkbox
= new LamiAxisCheckBoxOption(name
, defaultValue
, predicate
);
451 fYCheckBoxOptions
.add(checkbox
);
452 return fYCheckBoxOptions
.size() - 1;
456 * @return The final values of X series check boxes.
458 public boolean[] getXCheckBoxOptionValues() {
459 boolean[] selections
= new boolean[fXCheckBoxOptions
.size()];
460 if (selections
.length
!= 0) {
461 IntStream
.range(0, selections
.length
).forEach(i
-> selections
[i
] = fXCheckBoxOptions
.get(i
).getValue());
467 * @return The final values of Y series check boxes.
469 public boolean[] getYCheckBoxOptionValues() {
470 boolean[] selections
= new boolean[fYCheckBoxOptions
.size()];
471 if (selections
.length
!= 0) {
472 IntStream
.range(0, selections
.length
).forEach(i
-> selections
[i
] = fYCheckBoxOptions
.get(i
).getValue());
478 protected void okPressed() {
479 for (LamiAxisCheckBoxOption checkBox
: fXCheckBoxOptions
) {
480 checkBox
.updateValue();
482 for (LamiAxisCheckBoxOption checkBox
: fYCheckBoxOptions
) {
483 checkBox
.updateValue();
489 public Object
[] getResult() {
490 return series
.toArray();
493 private static <T
extends Comparable
<T
>> TableViewerColumn
createTableViewerColumn(TableViewer viewer
, String name
,
494 Function
<LamiXYSeriesDescription
, T
> propertyFunction
) {
495 TableViewerColumn viewerColumn
= new TableViewerColumn(viewer
, SWT
.CENTER
);
496 viewerColumn
.setLabelProvider(new ColumnLabelProvider() {
498 public @Nullable String
getText(@Nullable Object element
) {
499 if (element
!= null) {
500 return propertyFunction
.apply((LamiXYSeriesDescription
) element
).toString();
506 TableColumn column
= viewerColumn
.getColumn();
507 column
.setText(name
);