diff --git a/applications/databrowser/databrowser-plugins/org.csstudio.trends.databrowser2/src/org/csstudio/trends/databrowser2/waveformview/WaveformView.java b/applications/databrowser/databrowser-plugins/org.csstudio.trends.databrowser2/src/org/csstudio/trends/databrowser2/waveformview/WaveformView.java index 757d5b7c9d9..3f7140bb176 100644 --- a/applications/databrowser/databrowser-plugins/org.csstudio.trends.databrowser2/src/org/csstudio/trends/databrowser2/waveformview/WaveformView.java +++ b/applications/databrowser/databrowser-plugins/org.csstudio.trends.databrowser2/src/org/csstudio/trends/databrowser2/waveformview/WaveformView.java @@ -7,6 +7,8 @@ ******************************************************************************/ package org.csstudio.trends.databrowser2.waveformview; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import java.time.Instant; import java.util.ArrayList; import java.util.List; @@ -22,7 +24,6 @@ import org.csstudio.swt.rtplot.RTValuePlot; import org.csstudio.swt.rtplot.Trace; import org.csstudio.swt.rtplot.TraceType; -import org.csstudio.swt.rtplot.YAxis; import org.csstudio.swt.rtplot.data.TimeDataSearch; import org.csstudio.trends.databrowser2.Activator; import org.csstudio.trends.databrowser2.Messages; @@ -34,6 +35,7 @@ import org.csstudio.trends.databrowser2.model.ModelListenerAdapter; import org.csstudio.trends.databrowser2.model.PlotSample; import org.csstudio.trends.databrowser2.model.PlotSamples; +import org.csstudio.ui.util.widgets.MultipleSelectionCombo; import org.diirt.vtype.VNumberArray; import org.diirt.vtype.VType; import org.eclipse.jface.action.Action; @@ -46,12 +48,10 @@ import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Label; @@ -76,26 +76,26 @@ public class WaveformView extends DataBrowserAwareView /** Text used for the annotation that indicates waveform sample */ final public static String ANNOTATION_TEXT = "Waveform view"; - /** PV Name selector */ - private Combo pv_name; + /** PV Name(s) selector */ + private MultipleSelectionCombo pv_select; /** Plot */ private RTValuePlot plot; - /** Selector for model_item's current sample */ + /** Selector for first model_item current sample */ private Slider sample_index; - /** Timestamp of current sample. */ + /** Timestamp of current sample(s). */ private Text timestamp; - /** Status/severity of current sample. */ + /** Status/severity of current sample(s). */ private Text status; /** Model of the currently active Data Browser plot or null */ private Model model; - /** Annotation in plot that indicates waveform sample */ - private AnnotationInfo waveform_annotation; + /** Annotation(s) in data browser plot that indicate waveform sample(s) */ + private List waveform_annotations; private boolean changing_annotations = false; @@ -108,16 +108,23 @@ public class WaveformView extends DataBrowserAwareView @Override public void itemAdded(final ModelItem item) { - update(false); } @Override public void itemRemoved(final ModelItem item) { - if (item == model_item) { - model_item = null; + List oldSelectedItems = pv_select.getSelection(); + List items = new ArrayList<>(); + List selectedItems = new ArrayList<>(); + for(ModelItem modelItem : model.getItems()) + items.add(modelItem); + for(ModelItem modelItem : oldSelectedItems) { + if (modelItem != item) + selectedItems.add(modelItem); } + pv_select.setItems(items); + pv_select.setSelection(selectedItems); update(false); } @@ -146,16 +153,17 @@ public void changedAnnotations() public void changedTimerange() { // Update selected sample to assert that it's one of the visible ones. - if (model_item != null) - showSelectedSample(); + if (model_items != null && waveforms != null) + if (waveforms.size() > 0) + showSelectedSample(); } }; /** Selected model item in model, or null */ - private ModelItem model_item = null; + private List model_items = null; /** Waveform for the currently selected sample */ - private WaveformValueDataProvider waveform = null; + private List waveforms = null; /** {@inheritDoc} */ @Override @@ -186,23 +194,13 @@ protected void doCreatePartControl(final Composite parent) l.setText(Messages.SampleView_Item); l.setLayoutData(new GridData()); - pv_name = new Combo(parent, SWT.DROP_DOWN | SWT.READ_ONLY); - pv_name.setLayoutData(new GridData(SWT.FILL, 0, true, false, layout.numColumns-2, 1)); - pv_name.addSelectionListener(new SelectionListener() - { - @Override - public void widgetSelected(final SelectionEvent e) - { - widgetDefaultSelected(e); - } - + pv_select = new MultipleSelectionCombo<>(parent, 0); + pv_select.setItems(getModelItems()); + pv_select.setLayoutData(new GridData(SWT.FILL, 0, true, false, layout.numColumns-2, 1)); + pv_select.addPropertyChangeListener(new PropertyChangeListener() { @Override - public void widgetDefaultSelected(final SelectionEvent e) - { // First item is "--select PV name--" - if (pv_name.getSelectionIndex() == 0) - selectPV(null); - else - selectPV(model.getItem(pv_name.getText())); + public void propertyChange(PropertyChangeEvent evt) { + selectPV(pv_select.getSelection()); } }); @@ -214,11 +212,8 @@ public void widgetDefaultSelected(final SelectionEvent e) { @Override public void widgetSelected(final SelectionEvent e) - { // First item is "--select PV name--" - if (pv_name.getSelectionIndex() == 0) - selectPV(null); - else - selectPV(model.getItem(pv_name.getText())); + { + selectPV(pv_select.getSelection()); } }); @@ -267,13 +262,13 @@ public void widgetSelected(SelectionEvent e) getSite().registerContextMenu(mm, null); mm.addMenuListener(new IMenuListener(){ - @Override public void menuAboutToShow(IMenuManager manager) { mm.add(plot.getToolbarAction()); mm.add(plot.getLegendAction()); mm.add(new Separator()); mm.add(new ToggleYAxisAction(plot, true)); + mm.add(new ToggleYAxisAutoscaleAction(plot, true)); mm.add(new Separator()); mm.add(plot.getSnapshotAction()); } @@ -281,11 +276,26 @@ public void menuAboutToShow(IMenuManager manager) { } + /** Return List of selected ModelItems + * by finding Model Items from checked items in PV list + * @param names list of all PV names available in list + * @returns List of ModelItems + */ + private List getModelItems() + { + List modelItemList = new ArrayList<>(); + if (model != null) { + for (ModelItem item : model.getItems()) + modelItemList.add(item); + } + return modelItemList; + } + /** {@inheritDoc} */ @Override public void setFocus() { - pv_name.setFocus(); + pv_select.setFocus(); } /** {@inheritDoc} */ @@ -315,107 +325,129 @@ private void update(final boolean model_changed) { Display.getDefault().asyncExec( () -> { - if (pv_name.isDisposed()) - { + if (pv_select.isDisposed()) return; - } - if (model == null) - { // Clear/disable GUI - pv_name.setItems(new String[] { Messages.SampleView_NoPlot}); - pv_name.select(0); - pv_name.setEnabled(false); + + final List oldSelection = new ArrayList<>(pv_select.getSelection()); + pv_select.setItems(getModelItems()); + if (model == null) { + pv_select.setEnabled(false); selectPV(null); return; } - // Show PV names - final List names_list = new ArrayList<>(); - names_list.add(Messages.SampleView_SelectItem); - for (ModelItem item : model.getItems()) - names_list.add(item.getName()); - final String[] names = names_list.toArray(new String[names_list.size()]); - - // Is the previously selected item still valid? - final int selected = pv_name.getSelectionIndex(); - if (!model_changed && selected > 0 && model_item != null && pv_name.getText().equals(model_item.getName())) - { - // Show same PV name again in combo box - pv_name.setItems(names); - pv_name.select(selected); - pv_name.setEnabled(true); - return; - } - // Previously selected item no longer valid. // Show new items, clear rest - pv_name.setItems(names); - pv_name.select(0); - pv_name.setEnabled(true); - selectPV(null); + pv_select.setEnabled(true); + final List newSelection = new ArrayList<>(); + for(ModelItem oldItem : oldSelection) { + if (getModelItems().contains(oldItem)) + newSelection.add(oldItem); + } + pv_select.setSelection(newSelection); + selectPV(oldSelection); + }); } /** Select given PV item (or null). */ - private void selectPV(final ModelItem new_item) + private void selectPV(final List new_item) { - model_item = new_item; // Delete all existing traces for (Trace trace : plot.getTraces()) plot.removeTrace(trace); - // No or unknown PV name? - if (model_item == null) + model_items = new_item; + + removeAnnotation(); + + if (model_items == null || model_items.size() == 0) { - pv_name.setText(""); sample_index.setEnabled(false); - removeAnnotation(); return; } // Prepare to show waveforms of model item in plot - waveform = new WaveformValueDataProvider(); // Create trace for waveform - plot.addTrace(model_item.getResolvedDisplayName(), model_item.getUnits(), waveform, model_item.getColor(), TraceType.NONE, 1, PointType.CIRCLES, 5, 0); + waveforms = new ArrayList(); + for(int n=0; n yaxis : plot.getYAxes()) { - yaxis.setAutoscale(true); } + sample_index.setEnabled(true); + if (waveforms.size() > 0) + showSelectedSample(); + } /** Show the current sample of the current model item. */ private void showSelectedSample() { - // Get selected sample (= one waveform) - final PlotSamples samples = model_item.getSamples(); + final int numItems = model_items.size(); + final int idx = sample_index.getSelection(); - final PlotSample sample; - samples.getLock().lock(); - try - { - sample_index.setMaximum(samples.size()); - sample = samples.get(idx); - } - finally - { - samples.getLock().unlock(); - } - // Setting the value can be delayed while the plot is being updated - final VType value = sample.getVType(); - Activator.getThreadPool().execute(() -> waveform.setValue(value)); - if (value == null) - clearInfo(); - else - { - updateAnnotation(sample.getPosition(), sample.getValue()); - int size = value instanceof VNumberArray ? ((VNumberArray)value).getData().size() : 1; - plot.getXAxis().setValueRange(0.0, (double)size); - timestamp.setText(TimestampHelper.format(VTypeHelper.getTimestamp(value))); - status.setText(NLS.bind(Messages.SeverityStatusFmt, VTypeHelper.getSeverity(value).toString(), VTypeHelper.getMessage(value))); + + String timestampText = null; + String statusText = null; + + Instant firstWaveformSampleTime = null; + + for(int n=0; n waveforms.get(waveformIndex).setValue(value)); + if (value == null) + clearInfo(); + else + { + updateAnnotation(n, sample.getPosition(), sample.getValue()); + if (n == 0) { + timestampText = new String(); + statusText = new String(); + int size = value instanceof VNumberArray ? ((VNumberArray)value).getData().size() : 1; + plot.getXAxis().setValueRange(0.0, (double)size); + statusText += NLS.bind(Messages.SeverityStatusFmt, VTypeHelper.getSeverity(value).toString(), VTypeHelper.getMessage(value)); + timestampText += TimestampHelper.format(VTypeHelper.getTimestamp(value)); + firstWaveformSampleTime = VTypeHelper.getTimestamp(value); + } + else { + statusText += "; " + NLS.bind(Messages.SeverityStatusFmt, VTypeHelper.getSeverity(value).toString(), VTypeHelper.getMessage(value)); + timestampText += "; " + TimestampHelper.format(VTypeHelper.getTimestamp(value)); + } + } } + + timestamp.setText(timestampText); + status.setText(statusText); plot.requestUpdate(); } @@ -429,66 +461,78 @@ private void clearInfo() private void userMovedAnnotation() { - if (waveform_annotation == null) + if (waveform_annotations == null) return; for (AnnotationInfo annotation : model.getAnnotations()) { // Locate the annotation for this waveform - if (annotation.isInternal() && - annotation.getItemIndex() == waveform_annotation.getItemIndex() && - annotation.getText().equals(waveform_annotation.getText())) - { // Locate index of sample for annotation's time stamp - final PlotSamples samples = model_item.getSamples(); - final TimeDataSearch search = new TimeDataSearch(); - final int idx; - samples.getLock().lock(); - try - { - idx = search.findClosestSample(samples, annotation.getTime()); + for (AnnotationInfo waveform_annotation : waveform_annotations) { + if (annotation.isInternal() && + annotation.getItemIndex() == waveform_annotation.getItemIndex() && + annotation.getText().equals(waveform_annotation.getText())) + { // Locate index of sample for annotation's time stamp + // By first locating the relevant samples + for(ModelItem model_item : model_items) { + if (annotation.getText().contains(model_item.getDisplayName())) { + final PlotSamples samples = model_item.getSamples(); + final TimeDataSearch search = new TimeDataSearch(); + final int idx; + samples.getLock().lock(); + try + { + idx = search.findClosestSample(samples, annotation.getTime()); + } + finally + { + samples.getLock().unlock(); + } + // Update waveform view for that sample on UI thread + sample_index.getDisplay().asyncExec(() -> + { + sample_index.setSelection(idx); + showSelectedSample(); + }); + return; + } + } } - finally - { - samples.getLock().unlock(); - } - // Update waveform view for that sample on UI thread - sample_index.getDisplay().asyncExec(() -> - { - sample_index.setSelection(idx); - showSelectedSample(); - }); - return; } } } private void removeAnnotation() { - if (model != null) + if (model != null && waveform_annotations != null) { final List modelAnnotations = new ArrayList(model.getAnnotations()); - if (modelAnnotations.remove(waveform_annotation)) - { - changing_annotations = true; - model.setAnnotations(modelAnnotations); - changing_annotations = false; + for (AnnotationInfo waveform_annotation : waveform_annotations) { + if (modelAnnotations.remove(waveform_annotation)) + { + changing_annotations = true; + model.setAnnotations(modelAnnotations); + changing_annotations = false; + } } } - waveform_annotation = null; + waveform_annotations = null; } - private void updateAnnotation(final Instant time, final double value) + private void updateAnnotation(final int annotation_index, final Instant time, final double value) { + if (waveform_annotations == null) + waveform_annotations = new ArrayList(); + final List annotations = new ArrayList(model.getAnnotations()); // Initial annotation offset Point offset = new Point(20, -20); // If already in model, note its offset and remove for (AnnotationInfo annotation : annotations) { - if (annotation.getText().equals(ANNOTATION_TEXT)) + if (annotation.getText().equals(buildAnnotationText(annotation_index))) { // Update offset to where user last placed it offset = annotation.getOffset(); - waveform_annotation = annotation; - annotations.remove(waveform_annotation); + annotations.remove(annotation); break; + } } @@ -496,40 +540,61 @@ private void updateAnnotation(final Instant time, final double value) int item_index = 0; for (ModelItem item : model.getItems()) { - if (item == model_item) + if (item == model_items.get(annotation_index)) { item_index = i; break; } i++; } - waveform_annotation = new AnnotationInfo(true, item_index, time, value, offset, ANNOTATION_TEXT); - annotations.add(waveform_annotation); + waveform_annotations.add(annotation_index, new AnnotationInfo(true, item_index, time, value, offset, buildAnnotationText(annotation_index))); + annotations.add(waveform_annotations.get(annotation_index)); changing_annotations = true; model.setAnnotations(annotations); changing_annotations = false; } + private String buildAnnotationText(final Integer annotation_index) { + return ANNOTATION_TEXT + " " + model_items.get(annotation_index).getDisplayName(); + } + public class ToggleYAxisAction> extends Action { final private RTPlot plot; public ToggleYAxisAction(final RTPlot plot, final boolean is_visible) { - super(plot.getYAxes().get(0).isLogarithmic() ? "Linear Axis" : "Logarithmic Axis", null); + super("Logarithmic Axis", Action.AS_CHECK_BOX); this.plot = plot; + this.setChecked(plot.getYAxes().get(0).isLogarithmic()); + } + + @Override + public void run() + { + plot.getYAxes().get(0).setLogarithmic(!plot.getYAxes().get(0).isLogarithmic()); + plot.requestUpdate(); + this.setChecked(plot.getYAxes().get(0).isLogarithmic()); } + } - public void updateText() + public class ToggleYAxisAutoscaleAction> extends Action + { + final private RTPlot plot; + + public ToggleYAxisAutoscaleAction(final RTPlot plot, final boolean is_visible) { - setText(plot.getYAxes().get(0).isLogarithmic() ? "Linear Axis" : "Logarithmic Axis"); + super("Autoscaling", Action.AS_CHECK_BOX); + this.plot = plot; + this.setChecked(plot.getYAxes().get(0).isAutoscale()); } @Override public void run() { - plot.getYAxes().get(0).setLogarithmic(!plot.getYAxes().get(0).isLogarithmic()); + plot.getYAxes().get(0).setAutoscale(!plot.getYAxes().get(0).isAutoscale()); plot.requestUpdate(); + this.setChecked(plot.getYAxes().get(0).isAutoscale()); } } } diff --git a/core/ui/ui-plugins/org.csstudio.ui.util/src/org/csstudio/ui/util/widgets/MultipleSelectionCombo.java b/core/ui/ui-plugins/org.csstudio.ui.util/src/org/csstudio/ui/util/widgets/MultipleSelectionCombo.java index fce9ba43456..9ccfd76ac82 100644 --- a/core/ui/ui-plugins/org.csstudio.ui.util/src/org/csstudio/ui/util/widgets/MultipleSelectionCombo.java +++ b/core/ui/ui-plugins/org.csstudio.ui.util/src/org/csstudio/ui/util/widgets/MultipleSelectionCombo.java @@ -15,8 +15,6 @@ import java.util.List; import org.eclipse.swt.SWT; -import org.eclipse.swt.events.FocusAdapter; -import org.eclipse.swt.events.FocusEvent; import org.eclipse.swt.events.KeyAdapter; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.ModifyEvent; @@ -24,9 +22,11 @@ import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.events.ShellAdapter; +import org.eclipse.swt.events.ShellEvent; import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; -import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; @@ -76,7 +76,7 @@ public class MultipleSelectionCombo extends Composite { private Button drop_down; private Shell popup; - private org.eclipse.swt.widgets.List list; + private Button[] itemButtons; /** Items to show in list */ private List items = new ArrayList(); @@ -142,6 +142,7 @@ public void propertyChange(PropertyChangeEvent e) { text = new Text(this, SWT.BORDER); GridData gd = new GridData(SWT.FILL, 0, true, false); text.setLayoutData(gd); + text.setText("Select Items ..."); text.addModifyListener(new ModifyListener() { @Override public void modifyText(ModifyEvent e) { @@ -188,8 +189,8 @@ public void widgetSelected(final SelectionEvent e) { if (!isDropped()) drop(true); } - }); - } + }); + }; /** {@inheritDoc} */ @Override @@ -328,7 +329,13 @@ public List getSelection() { /** Update selection from list */ private void updateSelectionFromList() { - setSelection(list.getSelection()); + List selection = new ArrayList(); + if (itemButtons != null) { + for (int n=0; ntext to reflect selection */ @@ -340,6 +347,8 @@ private void updateText() { buf.append(stringRepresention(items.get(index))); } text.setText(buf.toString()); + if (selectionIndex.size() == 0) + text.setText("Select Items ..."); text.setSelection(buf.length()); } @@ -363,71 +372,65 @@ private void drop(boolean drop) { /** Create shell that simulates drop-down */ private void createPopup() { - popup = new Shell(getShell(), SWT.NO_TRIM | SWT.ON_TOP); - popup.setLayout(new FillLayout()); - list = new org.eclipse.swt.widgets.List(popup, SWT.MULTI | SWT.V_SCROLL); - list.setToolTipText(tool_tip); - - // Position popup under the text box - Rectangle bounds = text.getBounds(); - bounds.y += bounds.height; - // As high as necessary for items - bounds.height = 5 + 2 * list.getBorderWidth() + list.getItemHeight() - * items.size(); - // ..with limitation - bounds.height = Math.min(bounds.height, display.getBounds().height / 2); - // Map to screen coordinates - bounds = display.map(text, null, bounds); - popup.setBounds(bounds); - popup.open(); + popup = new Shell(getShell(), SWT.NO_TRIM); + popup.setLayout(new GridLayout()); + popup.setToolTipText(tool_tip); - // Update text from changed list selection - list.addSelectionListener(new SelectionListener() { + SelectionListener selectionListener = new SelectionListener() { @Override public void widgetSelected(SelectionEvent e) { updateSelectionFromList(); updateText(); } - @Override public void widgetDefaultSelected(SelectionEvent e) { updateSelectionFromList(); updateText(); hidePopup(); - } - }); + }}; + + int itemWidth = 0; + int itemHeight = 0; String[] stringItems = new String[items.size()]; for (int i = 0; i < items.size(); i++) { stringItems[i] = stringRepresention(items.get(i)); } - list.setItems(stringItems); - int[] intSelectionIndex = new int[selectionIndex.size()]; - for (int i = 0; i < intSelectionIndex.length; i++) { - intSelectionIndex[i] = selectionIndex.get(i); - } - list.setSelection(intSelectionIndex); - list.addKeyListener(new KeyAdapter() { + int numItems = stringItems.length; + itemButtons = new Button[numItems]; + for (int n=0; n