package org.simantics.charts.ui; import java.awt.Color; import java.text.Format; import java.util.ArrayList; import java.util.List; import java.util.Set; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.fieldassist.ControlDecoration; import org.eclipse.jface.fieldassist.FieldDecorationRegistry; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.layout.GridLayoutFactory; import org.eclipse.jface.preference.ColorSelector; import org.eclipse.jface.viewers.ArrayContentProvider; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.IDoubleClickListener; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.ListViewer; import org.eclipse.swt.SWT; import org.eclipse.swt.events.KeyAdapter; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Group; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Scale; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Text; import org.simantics.Simantics; import org.simantics.charts.Activator; import org.simantics.charts.query.ChartAndSubscriptionItemData; import org.simantics.charts.query.ChartAndSubscriptionItemReadQuery; import org.simantics.charts.ui.ChartData.ItemKey; import org.simantics.db.exception.DatabaseException; import org.simantics.modeling.ui.chart.property.ChartComposite; import org.simantics.trend.configuration.TrendItem; import org.simantics.trend.configuration.YAxisMode; import org.simantics.trend.impl.Plot; import org.simantics.utils.format.TimeFormat; import org.simantics.utils.ui.ISelectionUtils; /** * Dialog for chart properties: * *
 * Name:             [ Process ]
 * Time Start:       [   ]
 * Length:           [   ]
 * Scroll Increment: [--|--] n%
 * Axis Mode:        [Single/Multi]
 * [ ] Hairline Tracks Experiment Time
 * 
 * --- Styling ------------------------------------------
 * | [ ] Background Vertical Gradient {color1} {color2} |
 * | [ ] Show Grid                    {grid color}      |
 * | [ ] Show Milestones                                |
 * ------------------------------------------------------
 * 
 * --- Item visibility ---------------
 * | Visible          Hidden         |
 * | -----------      -------------- |
 * | |         | HIDE |            | |
 * | |         | SHOW |            | |
 * | -----------      -------------- |
 * -----------------------------------
 * 
* * @author toni.kalajainen@semantum.fi * @author Tuukka Lehtonen */ public class ChartDialog extends Dialog { private static final RGB DEFAULT_BG_COLOR1 = toRGB(Plot.PLOT_AREA_BG_GRADIENT_COLOR_BOTTOM.getColorComponents(new float[3]), null); private static final RGB DEFAULT_BG_COLOR2 = toRGB(Plot.PLOT_AREA_BG_GRADIENT_COLOR_TOP.getColorComponents(new float[3]), null); private static final RGB DEFAULT_GRID_COLOR = toRGB(Plot.GRID_LINE_COLOR.getColorComponents(new float[3]), null); Format timeFormat = new TimeFormat(100, 3); // UI Label lName, lStartTime, lLength, lScrollIncrement, lAxisMode; Text tName, tStartTime, tLength; Scale sIncrement; Combo cAxisMode; Button bBackgroundGradient; ColorSelector backgroundColor1; ColorSelector backgroundColor2; Button bShowGrid; ColorSelector gridColor; Button bMilestones; Button bTrackExperimentTime; ListViewer lVisibleItems; ListViewer lHiddenItems; ControlDecoration tStartTimeDecor; ControlDecoration tLengthDecor; final ChartData baseData = new ChartData(); final ChartData data; final Runnable applyAction; /** * Set when a chart item dialog has previously been opened by this dialog. */ ChartAndSubscriptionItemDialog currentItemDialog = null; public ChartDialog(Shell parentShell, ChartData data, Runnable applyAction) { super(parentShell); this.data = data; this.baseData.readFrom(data); this.applyAction = applyAction; setShellStyle(SWT.RESIZE | SWT.TITLE | SWT.CLOSE | SWT.BORDER); } @Override protected void createButtonsForButtonBar(Composite parent) { if (applyAction != null) createButton(parent, ChartDialogConstants.APPLY_ID, ChartDialogConstants.APPLY_LABEL, true); super.createButtonsForButtonBar(parent); } @Override protected Control createDialogArea(Composite parent) { Composite c = (Composite) super.createDialogArea(parent); GridLayoutFactory.fillDefaults().margins(8, 8).numColumns(9).applyTo(c); createDialogAreaContents(c); return c; } protected void createDialogAreaContents(Composite c) { GridDataFactory gd1 = GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).span(1, 1); GridDataFactory gd2 = GridDataFactory.fillDefaults().indent(5, 0).grab(true, false).span(8, 1); // 1st line - Chart: [chart name] lName = new Label(c, 0); lName.setText("&Name:"); gd1.applyTo(lName); tName = new Text(c, SWT.BORDER); tName.setEnabled( true ); if ( data.name != null ) tName.setText( data.name ); gd2.applyTo(tName); // 2nd line - start time + length Label l = new Label(c, 0); l.setText("Chart Time Window Preference (Auto-scale)"); GridDataFactory.fillDefaults().span(9, 1).applyTo(l); lStartTime = new Label(c, 0); lStartTime.setText("&Time Start:"); gd1.applyTo(lStartTime); tStartTime = new Text(c, SWT.BORDER); tStartTime.setToolTipText("Chart Window Fixed Start Time in Seconds or Yy Dd HH:mm:ss.ddd"); if ( data.timeStart!=null ) { String str = timeFormat.format(data.timeStart); tStartTime.setText(str); } gd2.applyTo(tStartTime); tStartTime.addModifyListener(new ModifyListener() { @Override public void modifyText(ModifyEvent e) { data.timeStart = parseTime(tStartTime, false, 0.0); validate(); } }); lLength = new Label(c, 0); lLength.setText("&Length:"); gd1.applyTo(lLength); tLength = new Text(c, SWT.BORDER); tLength.setToolTipText("Chart Window Fixed Time Axis Length in Seconds or Yy Dd HH:mm:ss.ddd"); if ( data.timeLength!=null ) { String str = timeFormat.format(data.timeLength); tLength.setText(str); } gd2.applyTo(tLength); tLength.addModifyListener(new ModifyListener() { @Override public void modifyText(ModifyEvent e) { data.timeLength = parseTime(tLength, false, 0.0); validate(); } }); // 3rd line - Increment scrollbar lScrollIncrement = new Label(c, 0); lScrollIncrement.setText("Scroll Increment:"); gd1.applyTo(lScrollIncrement); sIncrement = new Scale(c, SWT.HORIZONTAL); sIncrement.setMinimum(1); sIncrement.setMaximum(100); sIncrement.setIncrement(10); sIncrement.setPageIncrement(10); if (data.timeIncrement!=null) { sIncrement.setSelection( (int) (double) data.timeIncrement ); } gd2.copy().span(7, 1).applyTo(sIncrement); final Text sIncrementText = new Text(c, SWT.READ_ONLY | SWT.RIGHT); gd1.copy().hint(35, SWT.DEFAULT).applyTo(sIncrementText); sIncrement.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { sIncrementText.setText(sIncrement.getSelection() + " %"); } }); sIncrementText.setText(sIncrement.getSelection() + " %"); // 4th line -- AxisMode + Milestone lAxisMode = new Label(c, 0); lAxisMode.setText("A&xis Mode:"); gd1.applyTo(lAxisMode); cAxisMode = new Combo(c, SWT.DROP_DOWN | SWT.BORDER | SWT.READ_ONLY); GridDataFactory.fillDefaults().indent(5, 0).span(1, 1).applyTo(cAxisMode); cAxisMode.add("Single"); cAxisMode.add("Multi"); String _mode = data.axisMode==YAxisMode.SingleAxis ? "Single" : "Multi"; cAxisMode.setText( _mode ); // Filler GridDataFactory.fillDefaults().span(7, 1).applyTo(new Label(c, 0)); bTrackExperimentTime = new Button(c, SWT.CHECK); bTrackExperimentTime.setText("Ha&irline Tracks Experiment Time"); bTrackExperimentTime.setSelection( data.trackExperimentTime ); GridDataFactory.fillDefaults().span(9, 1).applyTo(bTrackExperimentTime); // Styling group begins Group gStyling = new Group(c, 0); gStyling.setText("Styling"); GridDataFactory.fillDefaults().indent(0, 5).grab(true, true).span(9, 1).applyTo(gStyling); GridLayoutFactory.fillDefaults().margins(8, 8).numColumns(4).applyTo(gStyling); // Background color settings { bBackgroundGradient = new Button(gStyling, SWT.CHECK); bBackgroundGradient.setText("&Background Vertical Gradient"); bBackgroundGradient.setToolTipText("Solid Color or Vertical Gradient"); bBackgroundGradient.setSelection(data.backgroundGradient); bBackgroundGradient.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { data.backgroundGradient = bBackgroundGradient.getSelection(); setBackgroundGradient(data.backgroundGradient); } }); GridDataFactory.fillDefaults().indent(0, 0).span(1, 1).grab(false, false).applyTo(bBackgroundGradient); backgroundColor1 = new ColorSelector(gStyling); backgroundColor2 = new ColorSelector(gStyling); backgroundColor1.getButton().setToolTipText("Select Gradient Bottom Color"); backgroundColor2.getButton().setToolTipText("Select Gradient Top Color"); GridDataFactory.fillDefaults().indent(0, 0).span(1, 1).grab(false, false).applyTo(backgroundColor1.getButton()); GridDataFactory.fillDefaults().indent(0, 0).span(1, 1).grab(false, false).applyTo(backgroundColor2.getButton()); backgroundColor1.setColorValue(toRGB(data.backgroundColor1, DEFAULT_BG_COLOR1)); backgroundColor2.setColorValue(toRGB(data.backgroundColor2, DEFAULT_BG_COLOR2)); setBackgroundGradient(data.backgroundGradient); Button bResetBackgroundColors = new Button(gStyling, SWT.PUSH); bResetBackgroundColors.setText("Reset"); bResetBackgroundColors.setToolTipText("Reset Background Color to Default"); bResetBackgroundColors.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { backgroundColor1.setColorValue(toRGB(null, DEFAULT_BG_COLOR1)); backgroundColor2.setColorValue(toRGB(null, DEFAULT_BG_COLOR2)); } }); } // Grid settings { bShowGrid = new Button(gStyling, SWT.CHECK); bShowGrid.setText("Show &Grid"); bShowGrid.setToolTipText("Toggle Background Grid Visibility"); bShowGrid.setSelection(data.showGrid); GridDataFactory.fillDefaults().indent(0, 0).span(1, 1).grab(false, false).applyTo(bShowGrid); gridColor = new ColorSelector(gStyling); gridColor.getButton().setToolTipText("Select Grid Color"); GridDataFactory.fillDefaults().indent(0, 0).span(1, 1).grab(false, false).applyTo(gridColor.getButton()); gridColor.setColorValue(toRGB(data.gridColor, DEFAULT_GRID_COLOR)); Button bResetGridColor = new Button(gStyling, SWT.PUSH); bResetGridColor.setText("Reset"); bResetGridColor.setToolTipText("Reset Grid Color to Default"); bResetGridColor.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { gridColor.setColorValue(toRGB(null, DEFAULT_GRID_COLOR)); } }); } // Grid settings { GridDataFactory.fillDefaults().span(1, 1).applyTo(new Label(gStyling, 0)); bMilestones = new Button(gStyling, SWT.CHECK); bMilestones.setText("Show &Milestones"); bMilestones.setToolTipText("Toggle Milestone Event Visibility"); bMilestones.setSelection( data.showMilestones ); GridDataFactory.fillDefaults().indent(0, 0).span(4, 1).applyTo(bMilestones); } // Item visibility group begins Group gItemVisibility = new Group(c, 0); gItemVisibility.setText("Item Visibility"); GridDataFactory.fillDefaults().indent(0, 5).grab(true, true).span(9, 1).applyTo(gItemVisibility); GridLayoutFactory.fillDefaults().margins(8, 8).numColumns(3).applyTo(gItemVisibility); Label visible = new Label(gItemVisibility, 0); visible.setText("Visible"); GridDataFactory.fillDefaults().grab(true, false).applyTo(visible); new Label(gItemVisibility, 0); Label hidden = new Label(gItemVisibility, 0); hidden.setText("Hidden"); GridDataFactory.fillDefaults().grab(true, false).applyTo(hidden); lVisibleItems = new ListViewer(gItemVisibility, SWT.MULTI | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL); GridDataFactory.fillDefaults().hint(200, 200).grab(true, true).applyTo(lVisibleItems.getControl()); Composite addRemoveButtons = new Composite(gItemVisibility, 0); GridLayoutFactory.fillDefaults().numColumns(1).applyTo(addRemoveButtons); GridDataFactory.fillDefaults().grab(false, true).span(1, 1).applyTo(addRemoveButtons); Button hideButton = new Button(addRemoveButtons, SWT.PUSH); hideButton.setText("&Hide \u2192"); GridDataFactory.fillDefaults().grab(true, false).span(1, 1).applyTo(hideButton); Button showButton = new Button(addRemoveButtons, SWT.PUSH); showButton.setText("\u2190 &Show"); GridDataFactory.fillDefaults().grab(true, false).span(1, 1).applyTo(showButton); lHiddenItems = new ListViewer(gItemVisibility, SWT.MULTI | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL); GridDataFactory.fillDefaults().hint(200, 200).grab(true, true).applyTo(lHiddenItems.getControl()); lVisibleItems.addDoubleClickListener(new OpenChartItemProperties()); lVisibleItems.setContentProvider(new ItemContentProvider(false)); lHiddenItems.addDoubleClickListener(new OpenChartItemProperties()); lHiddenItems.setContentProvider(new ItemContentProvider(true)); lVisibleItems.setLabelProvider(new ItemLabeler()); lHiddenItems.setLabelProvider(new ItemLabeler()); lVisibleItems.setInput(data); lHiddenItems.setInput(data); lVisibleItems.getList().addKeyListener(new SelectAllListener(lVisibleItems)); lHiddenItems.getList().addKeyListener(new SelectAllListener(lHiddenItems)); showButton.addSelectionListener(new ShowHideListener(false)); hideButton.addSelectionListener(new ShowHideListener(true)); Image image = FieldDecorationRegistry.getDefault() .getFieldDecoration(FieldDecorationRegistry.DEC_ERROR) .getImage(); tStartTimeDecor = createDecoration(tStartTime, SWT.LEFT | SWT.CENTER, image); tLengthDecor = createDecoration(tLength, SWT.LEFT | SWT.CENTER, image); } class ShowHideListener extends SelectionAdapter { private boolean hide; public ShowHideListener(boolean hide) { this.hide = hide; } @Override public void widgetSelected(SelectionEvent e) { ISelection selection = hide ? lVisibleItems.getSelection() : lHiddenItems.getSelection(); Set selected = ISelectionUtils.filterSetSelection(selection, ItemKey.class); if (!selected.isEmpty()) { if (hide) { data.hiddenItems.addAll(selected); } else { data.hiddenItems.removeAll(selected); } lVisibleItems.refresh(); lHiddenItems.refresh(); } } } static class SelectAllListener extends KeyAdapter { private ListViewer list; public SelectAllListener(ListViewer list) { this.list = list; } @Override public void keyPressed(KeyEvent e) { if (e.stateMask == SWT.CTRL && e.keyCode == 'a') { list.getList().selectAll(); } } }; class ItemContentProvider extends ArrayContentProvider { private final boolean expectedHidden; public ItemContentProvider(boolean expectedHidden) { this.expectedHidden = expectedHidden; } @Override public Object[] getElements(Object inputElement) { List result = new ArrayList<>(); for (ItemKey item : data.allItems.keySet()) if (data.hiddenItems.contains(item) == expectedHidden) result.add(item); return result.toArray(); } } private void setBackgroundGradient(boolean backgroundGradient) { backgroundColor2.setEnabled(backgroundGradient); backgroundColor1.getButton().setToolTipText(backgroundGradient ? "Select Gradient Bottom Color" : "Select Solid Color"); } protected Double parseTime(Text text, boolean returnDefault, double defaultValue) { if (text == null) return null; String s = text.getText(); Double time = ChartComposite.START_VALIDATOR.parse(s); if (time == null) return returnDefault ? defaultValue : null; return time; } protected ControlDecoration createDecoration(Control control, int position, Image image) { ControlDecoration d = new ControlDecoration(control, position); d.setMarginWidth(2); d.setImage(image); d.hide(); return d; } class ItemLabeler extends LabelProvider { @Override public String getText(Object element) { ItemKey item = (ItemKey) element; TrendItem ti = data.allItems.get(item); return ti.label; } } protected void parseUiToData() { data.name = tName.getText(); data.backgroundColor1 = toColorComponents( backgroundColor1.getColorValue() ); data.backgroundColor2 = toColorComponents( backgroundColor2.getColorValue() ); data.showGrid = bShowGrid.getSelection(); data.gridColor = toColorComponents( gridColor.getColorValue() ); data.showMilestones = bMilestones.getSelection(); data.trackExperimentTime = bTrackExperimentTime.getSelection(); data.axisMode = cAxisMode.getSelectionIndex()==0?YAxisMode.SingleAxis:YAxisMode.MultiAxis; data.timeIncrement = (double) sIncrement.getSelection(); data.timeLength = ChartComposite.LENGTH_VALIDATOR.parse(tLength.getText()); data.timeStart = ChartComposite.START_VALIDATOR.parse(tStartTime.getText()); } @Override protected void buttonPressed(int buttonId) { if (ChartDialogConstants.APPLY_ID == buttonId) { applyPressed(); } else { super.buttonPressed(buttonId); } } private void applyPressed() { if (applyAction != null) { parseUiToData(); if (!baseData.equals(data)) { baseData.readFrom(data); applyAction.run(); } } } @Override protected void okPressed() { parseUiToData(); if (applyAction != null && !baseData.equals(data)) applyAction.run(); super.okPressed(); } @Override protected void configureShell(Shell newShell) { super.configureShell(newShell); newShell.setText("Edit Chart"); } public void validate() { String startTimeError = ChartComposite.START_VALIDATOR.isValid(tStartTime.getText()); String lengthError = ChartComposite.LENGTH_VALIDATOR.isValid(tLength.getText()); setDecoration(tStartTimeDecor, startTimeError); setDecoration(tLengthDecor, lengthError); boolean ok = startTimeError == null && lengthError == null; setFinishable(ok); } private void setDecoration(ControlDecoration decor, String desc) { if (decor == null) return; if (desc != null) { decor.setDescriptionText(desc); decor.show(); } else { decor.hide(); } } protected void setFinishable(boolean ok) { getButton(OK).setEnabled(ok); } private static RGB toRGB(float[] components, RGB defaultValue) { if (components == null || components.length < 3) return defaultValue; Color c = new Color(components[0], components[1], components[2]); return new RGB(c.getRed(), c.getGreen(), c.getBlue()); } private static float[] toColorComponents(RGB rgb) { return new Color(rgb.red, rgb.green, rgb.blue).getColorComponents(new float[3]); } class OpenChartItemProperties implements IDoubleClickListener { @Override public void doubleClick(DoubleClickEvent event) { final ItemKey item = ISelectionUtils.filterSingleSelection(event.getSelection(), ItemKey.class); if (item == null) return; try { final ChartAndSubscriptionItemData data = Simantics.getSession().syncRequest( new ChartAndSubscriptionItemReadQuery(item.resource.getResource())); if (currentItemDialog != null && currentItemDialog.getShell() != null && !currentItemDialog.getShell().isDisposed()) { // TODO: reinitialize existing dialog with the specified data. // Close current dialog before opening new one. currentItemDialog.close(); } currentItemDialog = ChartDoubleClickHandler.openChartItemPropertiesDialog( event.getViewer().getControl().getShell(), item.resource.getResource(), data, false); } catch (DatabaseException e) { Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Problems opening chart item property dialog.")); } } } }