lami.ui: Replace LAMI charts with custom charts
[deliverable/tracecompass.git] / analysis / org.eclipse.tracecompass.analysis.lami.ui / src / org / eclipse / tracecompass / internal / provisional / analysis / lami / ui / views / LamiSeriesDialog.java
1 /*******************************************************************************
2 * Copyright (c) 2016 EfficiOS Inc., Jonathan Rajotte-Julien
3 *
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 *******************************************************************************/
9
10 package org.eclipse.tracecompass.internal.provisional.analysis.lami.ui.views;
11
12 import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
13
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
20 import org.eclipse.jdt.annotation.Nullable;
21 import org.eclipse.jface.dialogs.Dialog;
22 import org.eclipse.jface.dialogs.IDialogConstants;
23 import org.eclipse.jface.layout.TableColumnLayout;
24 import org.eclipse.jface.viewers.ArrayContentProvider;
25 import org.eclipse.jface.viewers.CheckboxTableViewer;
26 import org.eclipse.jface.viewers.ColumnLabelProvider;
27 import org.eclipse.jface.viewers.ColumnWeightData;
28 import org.eclipse.jface.viewers.ILabelProvider;
29 import org.eclipse.jface.viewers.IStructuredContentProvider;
30 import org.eclipse.jface.viewers.IStructuredSelection;
31 import org.eclipse.jface.viewers.TableViewer;
32 import org.eclipse.jface.viewers.TableViewerColumn;
33 import org.eclipse.swt.SWT;
34 import org.eclipse.swt.custom.SashForm;
35 import org.eclipse.swt.events.SelectionEvent;
36 import org.eclipse.swt.events.SelectionListener;
37 import org.eclipse.swt.layout.GridData;
38 import org.eclipse.swt.layout.GridLayout;
39 import org.eclipse.swt.widgets.Button;
40 import org.eclipse.swt.widgets.Composite;
41 import org.eclipse.swt.widgets.Control;
42 import org.eclipse.swt.widgets.Display;
43 import org.eclipse.swt.widgets.Group;
44 import org.eclipse.swt.widgets.Label;
45 import org.eclipse.swt.widgets.Shell;
46 import org.eclipse.swt.widgets.TableColumn;
47 import org.eclipse.swt.widgets.TableItem;
48 import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.aspect.LamiTableEntryAspect;
49 import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiChartModel.LamiChartType;
50 import org.eclipse.tracecompass.internal.provisional.analysis.lami.core.module.LamiXYSeriesDescription;
51 import org.eclipse.ui.dialogs.SelectionDialog;
52
53 /**
54 * Series creation dialog
55 *
56 * @author Jonathan Rajotte-Julien
57 */
58 public class LamiSeriesDialog extends SelectionDialog {
59
60 private static final int MINIMUM_COLUMN_WIDTH = 30;
61 private static final int MININAL_SERIES_TABLE_HEIGHT = 150;
62
63 /* The root element to populate the viewer with */
64 private final Object fXInputElement;
65 private final Object fYInputElement;
66 private final List<LamiXYSeriesDescription> series;
67
68 /* Providers for populating dialog */
69 private final ILabelProvider fXLabelProvider;
70 private final IStructuredContentProvider fXContentProvider;
71 private final ILabelProvider fYLabelProvider;
72 private final IStructuredContentProvider fYContentProvider;
73 private final IStructuredContentProvider fSeriesContentProvider;
74
75 private final boolean fRestrictXSeriesNumbers;
76
77 private final List<LamiAxisCheckBoxOption> fXCheckBoxOptions;
78 private final List<LamiAxisCheckBoxOption> fYCheckBoxOptions;
79
80 // the visual selection widget group
81 private TableViewer fXTableViewer;
82 private CheckboxTableViewer fYCheckBoxViewer;
83 private TableViewer fSeriesListViewer;
84
85 private Label fWarning;
86
87 /**
88 * @param parentShell
89 * The parent shell of the dialog
90 * @param chartType
91 * The chart type for which the dialog construct series
92 * @param xInput
93 * The possible X axis set of values
94 * @param yInput
95 * The possible Y axis set of values
96 * @param xContentProvider
97 * A content provider for the X axis set
98 * @param xLabelProvider
99 * The label provider for the X axis set
100 * @param yContentProvider
101 * The content provider for the Y axis set
102 * @param yLabelProvider
103 * The label provider for the Y axis set
104 */
105 public LamiSeriesDialog(Shell parentShell, LamiChartType chartType, Object xInput,
106 Object yInput,
107 IStructuredContentProvider xContentProvider,
108 ILabelProvider xLabelProvider,
109 IStructuredContentProvider yContentProvider,
110 ILabelProvider yLabelProvider) {
111 super(parentShell);
112 fXInputElement = xInput;
113 fYInputElement = yInput;
114 fXContentProvider = xContentProvider;
115 fXLabelProvider = xLabelProvider;
116 fYContentProvider = yContentProvider;
117 fYLabelProvider = yLabelProvider;
118 series = new ArrayList<>();
119 fSeriesContentProvider = checkNotNull(ArrayContentProvider.getInstance());
120
121 fXCheckBoxOptions = new ArrayList<>();
122 fYCheckBoxOptions = new ArrayList<>();
123 fSeriesListViewer = new TableViewer(parentShell);
124 fXTableViewer = new TableViewer(parentShell);
125 fYCheckBoxViewer = checkNotNull(CheckboxTableViewer.newCheckList(parentShell, SWT.NONE));
126
127 /* Dynamic restriction per chart type */
128 switch (chartType) {
129 case XY_SCATTER:
130 fRestrictXSeriesNumbers = false;
131 break;
132 case BAR_CHART:
133 case PIE_CHART:
134 default:
135 fRestrictXSeriesNumbers = true;
136 break;
137 }
138
139 this.fWarning = new Label(parentShell, SWT.NONE);
140 }
141
142 @Override
143 protected Control createDialogArea(@Nullable Composite parent) {
144
145 Composite composite = (Composite) super.createDialogArea(parent);
146 initializeDialogUnits(composite);
147
148 /* Base 3 column grid layout */
149 GridLayout gridLayout = new GridLayout(3, false);
150 composite.setLayout(gridLayout);
151
152 GridData gridData = new GridData(GridData.FILL_BOTH);
153 gridData.horizontalSpan = 3;
154 Group seriesGroup = new Group(composite, SWT.NONE);
155 seriesGroup.setLayoutData(gridData);
156 seriesGroup.setLayout(new GridLayout(3, false));
157 seriesGroup.setText(Messages.LamiSeriesDialog_series);
158
159 /*
160 * New sub group for the series table.
161 */
162 gridData = new GridData(GridData.FILL_BOTH);
163 gridData.horizontalSpan = 2;
164 gridData.heightHint = MININAL_SERIES_TABLE_HEIGHT;
165 Group seriesTableGroup = new Group(seriesGroup, SWT.NONE);
166 seriesTableGroup.setLayoutData(gridData);
167 TableColumnLayout layout = new TableColumnLayout();
168 seriesTableGroup.setLayout(layout);
169
170 /* Current series */
171 fSeriesListViewer = new TableViewer(seriesTableGroup, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
172 fSeriesListViewer.setContentProvider(fSeriesContentProvider);
173 fSeriesListViewer.setInput(series);
174 fSeriesListViewer.getTable().setHeaderVisible(true);
175 fSeriesListViewer.getTable().setLinesVisible(true);
176 TableViewerColumn column1 = createTableViewerColumn(fSeriesListViewer, Messages.LamiSeriesDialog_x_values, element -> element.getXAspect().getLabel());
177 TableViewerColumn column2 = createTableViewerColumn(fSeriesListViewer, Messages.LamiSeriesDialog_y_values, element -> element.getYAspect().getLabel());
178 layout.setColumnData(column1.getColumn(), new ColumnWeightData(1, MINIMUM_COLUMN_WIDTH, true));
179 layout.setColumnData(column2.getColumn(), new ColumnWeightData(1, MINIMUM_COLUMN_WIDTH, true));
180
181 /* Delete series button */
182 gridData = new GridData(GridData.CENTER);
183 gridData.horizontalSpan = 1;
184 Button deleteSeries = new Button(seriesGroup, SWT.PUSH);
185 deleteSeries.setText(Messages.LamiSeriesDialog_delete);
186 deleteSeries.setLayoutData(gridData);
187 deleteSeries.addSelectionListener(new SelectionListener() {
188 @Override
189 public void widgetSelected(@Nullable SelectionEvent e) {
190 /* Remove the selectecd series */
191 IStructuredSelection selections = (IStructuredSelection) fSeriesListViewer.getSelection();
192 for (Object selection : selections.toList()) {
193 series.remove(selection);
194 }
195 /* When table is empty reset to initial state */
196 if (series.isEmpty()) {
197 /* Make sure the OK button is disabled */
198 getButton(IDialogConstants.OK_ID).setEnabled(false);
199 /* Hide the selection warning */
200 fWarning.setVisible(false);
201
202 /*
203 * Reset the initial selection of the X axis selection table
204 */
205 fXTableViewer.refresh();
206 /* Reset check boxes options */
207 fXCheckBoxOptions.forEach(checkBox -> {
208 checkBox.setButtonEnabled(true);
209 });
210 fYCheckBoxOptions.forEach(checkBox -> {
211 checkBox.setButtonEnabled(true);
212 });
213 }
214 /* Refresh the series table to show the added series */
215 fSeriesListViewer.refresh();
216 }
217
218 @Override
219 public void widgetDefaultSelected(@Nullable SelectionEvent e) {
220 }
221 });
222
223 /*
224 * Series creator subgroup
225 */
226 gridData = new GridData(GridData.FILL_BOTH);
227 gridData.horizontalSpan = 3;
228 Group seriesCreatorGroup = new Group(composite, getShellStyle());
229 seriesCreatorGroup.setLayoutData(gridData);
230 seriesCreatorGroup.setLayout(new GridLayout(3, false));
231 seriesCreatorGroup.setText(Messages.LamiSeriesDialog_serie_creator);
232
233 /* X axis sash label */
234 gridData = new GridData(GridData.FILL_BOTH | GridData.VERTICAL_ALIGN_END);
235 gridData.horizontalSpan = 1;
236 Label xSeriesCreatorLabel = new Label(seriesCreatorGroup, SWT.CENTER);
237 xSeriesCreatorLabel.setLayoutData(gridData);
238 xSeriesCreatorLabel.setText(Messages.LamiSeriesDialog_x_axis);
239
240 gridData = new GridData(GridData.FILL_BOTH | GridData.VERTICAL_ALIGN_END);
241 gridData.horizontalSpan = 1;
242 Label ySeriesCreatorLabel = new Label(seriesCreatorGroup, SWT.CENTER);
243 ySeriesCreatorLabel.setLayoutData(gridData);
244 ySeriesCreatorLabel.setText(Messages.LamiSeriesDialog_y_axis);
245
246 /* Empty label for grid layout */
247 gridData = new GridData(GridData.FILL_BOTH);
248 gridData.horizontalSpan = 1;
249 Label emptyLabel = new Label(seriesCreatorGroup, SWT.CENTER);
250 emptyLabel.setLayoutData(gridData);
251
252 SashForm sash1 = new SashForm(seriesCreatorGroup, SWT.BORDER | SWT.HORIZONTAL);
253 gridData = new GridData(GridData.FILL_BOTH);
254 gridData.horizontalSpan = 2;
255 sash1.setLayoutData(gridData);
256 sash1.setVisible(true);
257
258 fXTableViewer = new TableViewer(sash1, getTableStyle());
259 fXTableViewer.setContentProvider(fXContentProvider);
260 fXTableViewer.setLabelProvider(fXLabelProvider);
261 fXTableViewer.setInput(fXInputElement);
262
263 fYCheckBoxViewer = checkNotNull(CheckboxTableViewer.newCheckList(sash1, SWT.BORDER));
264 fYCheckBoxViewer.setLabelProvider(fYLabelProvider);
265 fYCheckBoxViewer.setContentProvider(fYContentProvider);
266 fYCheckBoxViewer.setInput(fYInputElement);
267
268 gridData = new GridData(SWT.FILL, SWT.NONE, true, true);
269 gridData.horizontalSpan = 1;
270 Button button1 = new Button(seriesCreatorGroup, SWT.PUSH);
271 button1.setText(Messages.LamiSeriesDialog_add);
272 button1.setLayoutData(gridData);
273 button1.addSelectionListener(new SelectionListener() {
274
275 @Override
276 public void widgetSelected(@Nullable SelectionEvent e) {
277 Object[] ySelections = fYCheckBoxViewer.getCheckedElements();
278 IStructuredSelection xSelections = (IStructuredSelection) fXTableViewer.getSelection();
279 @Nullable Object x = xSelections.getFirstElement();
280 if (!(x instanceof LamiTableEntryAspect) || ySelections.length == 0) {
281 return;
282 }
283
284 /* Add selection to series if it doesn not already exist in the list */
285 for (Object y : ySelections) {
286 if(!(y instanceof LamiTableEntryAspect)) {
287 continue;
288 }
289 LamiXYSeriesDescription serie = new LamiXYSeriesDescription((LamiTableEntryAspect) x, ((LamiTableEntryAspect) y));
290 if (!series.contains(serie)) {
291 series.add(serie);
292 fSeriesListViewer.refresh();
293 }
294 }
295
296 /* Set label warning visible and enable OK button */
297 fWarning.setVisible(true);
298 getButton(IDialogConstants.OK_ID).setEnabled(true);
299
300 /* Update possible X selection based on current series */
301 TableItem[] items = fXTableViewer.getTable().getItems();
302 Arrays.stream(items).forEach(item -> {
303 LamiTableEntryAspect aspect = (LamiTableEntryAspect) item.getData();
304 if (!aspect.arePropertiesEqual(series.get(0).getXAspect())) {
305 fXTableViewer.remove(aspect);
306 }
307 if (fRestrictXSeriesNumbers && aspect != (series.get(0).getXAspect())) {
308 fXTableViewer.remove(aspect);
309 }
310 });
311
312 /*
313 * Disable all checkBox that do not apply to aspects series.
314 * Simply take the first one since all series should comply to
315 * the same restriction
316 */
317 fXCheckBoxOptions.forEach(checkBox -> {
318 checkBox.setButtonEnabled(checkBox.getPredicate().test(series.get(0).getXAspect()));
319 });
320 fYCheckBoxOptions.forEach(checkBox -> {
321 checkBox.setButtonEnabled(checkBox.getPredicate().test(series.get(0).getYAspect()));
322 });
323 }
324
325 @Override
326 public void widgetDefaultSelected(@Nullable SelectionEvent e) {
327 }
328 });
329
330
331 gridData = new GridData(GridData.FILL_BOTH | GridData.VERTICAL_ALIGN_END);
332 gridData.horizontalSpan = 3;
333 fWarning = new Label(seriesCreatorGroup, SWT.LEFT);
334 fWarning.setLayoutData(gridData);
335 fWarning.setText(Messages.LamiSeriesDialog_selectionRestrictionWarning);
336 fWarning.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_RED));
337 fWarning.setVisible(false);
338
339 gridData = new GridData(GridData.FILL_BOTH);
340 gridData.horizontalSpan = 3;
341 Group optionGroups = new Group(composite, getShellStyle());
342 optionGroups.setLayoutData(gridData);
343 optionGroups.setLayout(new GridLayout(3, false));
344 optionGroups.setText(Messages.LamiSeriesDialog_chart_options);
345
346 for (LamiAxisCheckBoxOption checkBox : fXCheckBoxOptions) {
347 Button button = new Button(optionGroups, SWT.CHECK);
348 button.setSelection(checkBox.getDefaultValue());
349 button.setText(checkBox.getName());
350 checkBox.setButton(button);
351 }
352
353 for (LamiAxisCheckBoxOption checkBox : fYCheckBoxOptions) {
354 Button button = new Button(optionGroups, SWT.CHECK);
355 button.setSelection(checkBox.getDefaultValue());
356 button.setText(checkBox.getName());
357 checkBox.setButton(button);
358 }
359
360 fYCheckBoxViewer.getTable().addSelectionListener(new SelectionListener() {
361
362 @Override
363 public void widgetSelected(@Nullable SelectionEvent e) {
364 /* On check */
365 if (e != null && e.detail == SWT.CHECK) {
366 /* Change possible selection */
367 IStructuredSelection selections = (IStructuredSelection) fYCheckBoxViewer.getSelection();
368 if (selections.getFirstElement() != null) {
369
370 boolean checked = fYCheckBoxViewer.getChecked(selections.getFirstElement());
371 /*
372 * If just selected look for stuff to disable. If not no
373 * need to look for stuff to disable since it was
374 * already done before.
375 */
376 if (checked) {
377 TableItem[] items = fYCheckBoxViewer.getTable().getItems();
378 Arrays.stream(items).forEach(item -> {
379 LamiTableEntryAspect aspect = (LamiTableEntryAspect) item.getData();
380 if (!aspect.arePropertiesEqual((LamiTableEntryAspect) checkNotNull(selections.getFirstElement()))) {
381 fYCheckBoxViewer.remove(aspect);
382 }
383 });
384 } else if (!checked && fYCheckBoxViewer.getCheckedElements().length == 0 && fSeriesListViewer.getTable().getItemCount() == 0) {
385 fYCheckBoxViewer.refresh();
386 }
387 }
388 }
389 }
390
391 @Override
392 public void widgetDefaultSelected(@Nullable SelectionEvent e) {
393 }
394 });
395
396 Dialog.applyDialogFont(composite);
397 return composite;
398 }
399
400 /*
401 * Disable OK button on dialog creation.
402 */
403 @Override
404 protected void createButtonsForButtonBar(@Nullable Composite parent) {
405 super.createButtonsForButtonBar(parent);
406 getButton(IDialogConstants.OK_ID).setEnabled(false);
407 }
408
409 /**
410 * Return the style flags for the table viewer.
411 *
412 * @return int
413 */
414 protected int getTableStyle() {
415 return SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER;
416 }
417
418 /**
419 * Add check box option for X series.
420 *
421 * @param name
422 * The name of the option. The actual text shown to the user.
423 * @param defaultValue
424 * The default state of the check box option.
425 * @param predicate
426 * The predicate to check if the option applies to the given
427 * aspect
428 * @return The index of the option value in the result table.
429 */
430 public int addXCheckBoxOption(String name, boolean defaultValue, Predicate<LamiTableEntryAspect> predicate) {
431 LamiAxisCheckBoxOption checkBox = new LamiAxisCheckBoxOption(name, defaultValue, predicate);
432 fXCheckBoxOptions.add(checkBox);
433 return fXCheckBoxOptions.size() - 1;
434 }
435
436 /**
437 * Add check box option for Y series.
438 *
439 * @param name
440 * The name of the option. The actual text shown to the user.
441 * @param defaultValue
442 * The default state of the check box option.
443 * @param predicate
444 * The predicate to check if the option applies to the given
445 * aspect
446 * @return The index of the option value in the result table.
447 */
448 public int addYCheckBoxOption(String name, boolean defaultValue, Predicate<LamiTableEntryAspect> predicate) {
449 LamiAxisCheckBoxOption checkbox = new LamiAxisCheckBoxOption(name, defaultValue, predicate);
450 fYCheckBoxOptions.add(checkbox);
451 return fYCheckBoxOptions.size() - 1;
452 }
453
454 /**
455 * @return The final values of X series check boxes.
456 */
457 public boolean[] getXCheckBoxOptionValues() {
458 boolean[] selections = new boolean[fXCheckBoxOptions.size()];
459 for (int i = 0; i < selections.length; i++) {
460 selections[i] = fXCheckBoxOptions.get(i).getValue();
461 }
462 return selections;
463 }
464
465 /**
466 * @return The final values of Y series check boxes.
467 */
468 public boolean[] getYCheckBoxOptionValues() {
469 boolean[] selections = new boolean[fYCheckBoxOptions.size()];
470 for (int i = 0; i < selections.length; i++) {
471 selections[i] = fYCheckBoxOptions.get(i).getValue();
472 }
473 return selections;
474 }
475
476 @Override
477 protected void okPressed() {
478 for (LamiAxisCheckBoxOption checkBox : fXCheckBoxOptions) {
479 checkBox.updateValue();
480 }
481 for (LamiAxisCheckBoxOption checkBox : fYCheckBoxOptions) {
482 checkBox.updateValue();
483 }
484 super.okPressed();
485 }
486
487 @Override
488 public Object[] getResult() {
489 return series.toArray();
490 }
491
492 private static <T extends Comparable<T>> TableViewerColumn createTableViewerColumn(TableViewer viewer, String name,
493 Function<LamiXYSeriesDescription, T> propertyFunction) {
494 TableViewerColumn viewerColumn = new TableViewerColumn(viewer, SWT.CENTER);
495 viewerColumn.setLabelProvider(new ColumnLabelProvider() {
496 @Override
497 public @Nullable String getText(@Nullable Object element) {
498 if (element != null) {
499 return propertyFunction.apply((LamiXYSeriesDescription) element).toString();
500 }
501 return null;
502 }
503 });
504
505 TableColumn column = viewerColumn.getColumn();
506 column.setText(name);
507 return viewerColumn;
508 }
509
510 }
This page took 0.044584 seconds and 5 git commands to generate.