From 4faad5c7d730eb044ceb0af6f1a815fc3c076ea1 Mon Sep 17 00:00:00 2001 From: luukkainen Date: Fri, 18 Jan 2013 11:18:04 +0000 Subject: [PATCH] StringChooser for selecting objects with labels. Replaced TrackedText with StingChooser in Bar/PiePropertyComposite2. refs #3988 fixes #3996 git-svn-id: https://www.simantics.org/svn/simantics/sysdyn/trunk@26647 ac1ea38d-2e2b-0410-8846-a27921b304fc --- .../chart/properties/ChartVariable.java | 24 +- .../properties/ChartVariableFactory.java | 34 + ...difier.java => ChartVariableModifier.java} | 48 +- .../properties/IdLabelProposalProvider.java | 114 ---- .../chart/properties/RVIModifier.java | 3 +- .../chart/properties/StringChooser.java | 590 ++++++++++++++++++ .../properties/StringChooserModifyEvent.java | 32 + .../StringChooserModifyListener.java | 7 + .../StringChooserModifyListenerImpl.java | 71 +++ .../chart/properties/VariableFactory.java | 29 - .../properties/VariableProposalProvider.java | 111 +++- .../bar/BarSeriesPropertyComposite2.java | 39 +- .../pie/PieSeriesPropertyComposite2.java | 26 +- 13 files changed, 907 insertions(+), 221 deletions(-) create mode 100644 org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/ChartVariableFactory.java rename org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/{VariableModifier.java => ChartVariableModifier.java} (66%) delete mode 100644 org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/IdLabelProposalProvider.java create mode 100644 org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/StringChooser.java create mode 100644 org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/StringChooserModifyEvent.java create mode 100644 org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/StringChooserModifyListener.java create mode 100644 org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/StringChooserModifyListenerImpl.java delete mode 100644 org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/VariableFactory.java diff --git a/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/ChartVariable.java b/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/ChartVariable.java index 994e7462..1498b47a 100644 --- a/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/ChartVariable.java +++ b/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/ChartVariable.java @@ -29,17 +29,17 @@ public class ChartVariable { return rvi; } -// @Override -// public int hashCode() { -// return rvi.hashCode(); -// } -// -// @Override -// public boolean equals(Object obj) { -// if (obj.getClass() != getClass()) -// return false; -// ChartVariable other = (ChartVariable)obj; -// return rvi.equals(other.rvi); -// } + @Override + public int hashCode() { + return rvi.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj.getClass() != getClass()) + return false; + ChartVariable other = (ChartVariable)obj; + return rvi.equals(other.rvi); + } } diff --git a/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/ChartVariableFactory.java b/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/ChartVariableFactory.java new file mode 100644 index 00000000..fea54159 --- /dev/null +++ b/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/ChartVariableFactory.java @@ -0,0 +1,34 @@ +package org.simantics.jfreechart.chart.properties; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import org.simantics.browsing.ui.swt.widgets.impl.ReadFactoryImpl; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.exception.DatabaseException; +import org.simantics.sysdyn.JFreeChartResource; + +public class ChartVariableFactory extends ReadFactoryImpl{ + + private Map map; + + public ChartVariableFactory(Collection data) { + map = new HashMap(); + for (ChartVariable v : data) { + map.put(v.getRvi(), v); + } + } + + @Override + public ChartVariable perform(ReadGraph graph, Resource input) + throws DatabaseException { + String rvi = graph.getPossibleRelatedValue(input, JFreeChartResource.getInstance(graph).variableRVI); + if (rvi == null) + return null; + return map.get(rvi); + + } + +} diff --git a/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/VariableModifier.java b/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/ChartVariableModifier.java similarity index 66% rename from org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/VariableModifier.java rename to org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/ChartVariableModifier.java index 84de0b05..a1e13dd0 100644 --- a/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/VariableModifier.java +++ b/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/ChartVariableModifier.java @@ -8,8 +8,6 @@ import org.eclipse.jface.fieldassist.IContentProposalListener; import org.eclipse.jface.fieldassist.IContentProposalListener2; import org.eclipse.jface.fieldassist.TextContentAdapter; import org.eclipse.swt.widgets.Control; -import org.simantics.browsing.ui.swt.widgets.impl.TextModifyListenerImpl; -import org.simantics.browsing.ui.swt.widgets.impl.TrackedModifyEvent; import org.simantics.browsing.ui.swt.widgets.impl.WidgetSupport; import org.simantics.databoard.Bindings; import org.simantics.db.Resource; @@ -17,7 +15,7 @@ import org.simantics.db.WriteGraph; import org.simantics.db.exception.DatabaseException; import org.simantics.sysdyn.JFreeChartResource; -public class VariableModifier extends TextModifyListenerImpl { +public class ChartVariableModifier extends StringChooserModifyListenerImpl { private boolean active; private Control control; @@ -27,7 +25,7 @@ public class VariableModifier extends TextModifyListenerImpl { 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','Å','Ä','Ö', '1','2','3','4','5','6','7','8','9','0','.','_'}; - public VariableModifier(Control control, WidgetSupport support) { + public ChartVariableModifier(Control control, WidgetSupport support) { this.control = control; this.active = true; @@ -39,7 +37,7 @@ public class VariableModifier extends TextModifyListenerImpl { } //SimpleContentProposalProvider scpp = new VariableProposalProvider(control, support); - IdLabelProposalProvider scpp = new VariableProposalProvider(control, support); + VariableProposalProvider scpp = new VariableProposalProvider(control, support); scpp.setFiltering(true); ContentProposalAdapter adapter = new ContentProposalAdapter( @@ -50,14 +48,14 @@ public class VariableModifier extends TextModifyListenerImpl { @Override public void proposalPopupOpened(ContentProposalAdapter adapter) { - if(VariableModifier.this != null) - VariableModifier.this.deactivate(); + if(ChartVariableModifier.this != null) + ChartVariableModifier.this.deactivate(); } @Override public void proposalPopupClosed(ContentProposalAdapter adapter) { - if(VariableModifier.this != null) - VariableModifier.this.activate(); + if(ChartVariableModifier.this != null) + ChartVariableModifier.this.activate(); } }); @@ -66,30 +64,32 @@ public class VariableModifier extends TextModifyListenerImpl { @Override public void proposalAccepted(IContentProposal proposal) { - if(VariableModifier.this.control != null && !VariableModifier.this.control.isDisposed()) - VariableModifier.this.modifyText(new TrackedModifyEvent(VariableModifier.this.control, proposal.getLabel())); + if(ChartVariableModifier.this.control != null && !ChartVariableModifier.this.control.isDisposed()) + ChartVariableModifier.this.modifySelection(new StringChooserModifyEvent(ChartVariableModifier.this.control, new ChartVariable(proposal.getContent(), proposal.getLabel()), proposal.getLabel())); } }); } - - - @Override - public void applyText(WriteGraph graph, Resource resource, String text) throws DatabaseException { - if(active) { + + @Override + public void applyObject(WriteGraph graph, Resource resource, ChartVariable object, String text) throws DatabaseException { + if(active) { JFreeChartResource jfree = JFreeChartResource.getInstance(graph); - graph.claimLiteral(resource, jfree.variableRVI, text, Bindings.STRING); + graph.claimLiteral(resource, jfree.variableRVI, object.getRvi(), Bindings.STRING); graph.deny(resource, jfree.variableFilter); } - } + + } + - public void deactivate() { - active = false; - } - public void activate() { - active = true; - } + public void deactivate() { + active = false; + } + + public void activate() { + active = true; + } } diff --git a/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/IdLabelProposalProvider.java b/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/IdLabelProposalProvider.java deleted file mode 100644 index f643e70c..00000000 --- a/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/IdLabelProposalProvider.java +++ /dev/null @@ -1,114 +0,0 @@ -package org.simantics.jfreechart.chart.properties; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; - -import org.eclipse.jface.fieldassist.ContentProposal; -import org.eclipse.jface.fieldassist.IContentProposal; -import org.eclipse.jface.fieldassist.IContentProposalProvider; -import org.simantics.utils.datastructures.Pair; - -public class IdLabelProposalProvider implements IContentProposalProvider{ - - /* - * The proposals provided. - */ - private Collection proposals; - - /* - * The proposals mapped to IContentProposal. Cached for speed in the case - * where filtering is not used. - */ - private IContentProposal[] contentProposals; - - /* - * Boolean that tracks whether filtering is used. - */ - private boolean filterProposals = false; - - /** - * Construct a SimpleContentProposalProvider whose content proposals are - * always the specified array of Objects. - * - * @param proposals - * the array of Strings to be returned whenever proposals are - * requested. - */ - public IdLabelProposalProvider(Collection proposals) { - super(); - this.proposals = proposals; - } - - /** - * Return an array of Objects representing the valid content proposals for a - * field. - * - * @param contents - * the current contents of the field (only consulted if filtering - * is set to true) - * @param position - * the current cursor position within the field (ignored) - * @return the array of Objects that represent valid proposals for the field - * given its current content. - */ - @SuppressWarnings("unchecked") - public IContentProposal[] getProposals(String contents, int position) { - if (filterProposals) { - ArrayList list = new ArrayList(); - for (ChartVariable proposal : proposals) { - if (proposal.getRvi().length() >= contents.length() && proposal.getRvi().substring(0, contents.length()).equalsIgnoreCase(contents)) { - if (proposal.getLabel() != null) - list.add(new ContentProposal(proposal.getRvi(),proposal.getLabel(), null)); - else - list.add(new ContentProposal(proposal.getRvi())); - } else if (proposal.getLabel() != null && proposal.getLabel().length() >= contents.length() && proposal.getLabel().substring(0, contents.length()).equalsIgnoreCase(contents)) { - list.add(new ContentProposal(proposal.getRvi(),proposal.getLabel(), null)); - } - } - - return (IContentProposal[]) list.toArray(new IContentProposal[list - .size()]); - } - if (contentProposals == null) { - contentProposals = new IContentProposal[proposals.size()]; - Iterator iter = proposals.iterator(); - for (int i = 0; i < proposals.size(); i++) { - ChartVariable proposal = iter.next(); - if (proposal.getLabel() != null) - contentProposals[i] = new ContentProposal(proposal.getRvi(),proposal.getLabel(),null); - else - contentProposals[i] = new ContentProposal(proposal.getRvi()); - } - } - return contentProposals; - } - - /** - * Set the Strings to be used as content proposals. - * - * @param items - * the array of Strings to be used as proposals. - */ - public void setProposals(Collection items) { - this.proposals = items; - contentProposals = null; - } - - /** - * Set the boolean that controls whether proposals are filtered according to - * the current field content. - * - * @param filterProposals - * true if the proposals should be filtered to - * show only those that match the current contents of the field, - * and false if the proposals should remain the - * same, ignoring the field content. - * @since 3.3 - */ - public void setFiltering(boolean filterProposals) { - this.filterProposals = filterProposals; - // Clear any cached proposals. - contentProposals = null; - } -} diff --git a/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/RVIModifier.java b/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/RVIModifier.java index b9359e0d..81edb2b9 100644 --- a/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/RVIModifier.java +++ b/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/RVIModifier.java @@ -17,7 +17,6 @@ import org.eclipse.jface.fieldassist.ContentProposalAdapter; import org.eclipse.jface.fieldassist.IContentProposal; import org.eclipse.jface.fieldassist.IContentProposalListener; import org.eclipse.jface.fieldassist.IContentProposalListener2; -import org.eclipse.jface.fieldassist.SimpleContentProposalProvider; import org.eclipse.jface.fieldassist.TextContentAdapter; import org.eclipse.swt.widgets.Control; import org.simantics.browsing.ui.swt.widgets.impl.TextModifyListenerImpl; @@ -63,7 +62,7 @@ public class RVIModifier extends TextModifyListenerImpl { } //SimpleContentProposalProvider scpp = new VariableProposalProvider(control, support); - IdLabelProposalProvider scpp = new VariableProposalProvider(control, support); + VariableProposalProvider scpp = new VariableProposalProvider(control, support); scpp.setFiltering(true); ContentProposalAdapter adapter = new ContentProposalAdapter( diff --git a/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/StringChooser.java b/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/StringChooser.java new file mode 100644 index 00000000..892e9986 --- /dev/null +++ b/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/StringChooser.java @@ -0,0 +1,590 @@ +package org.simantics.jfreechart.chart.properties; + +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.ListenerList; +import org.eclipse.jface.dialogs.IInputValidator; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.resource.LocalResourceManager; +import org.eclipse.jface.resource.ResourceManager; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.events.FocusListener; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.KeyListener; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.MouseEvent; +import org.eclipse.swt.events.MouseListener; +import org.eclipse.swt.events.MouseTrackListener; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Text; +import org.simantics.browsing.ui.swt.widgets.DefaultColorProvider; +import org.simantics.browsing.ui.swt.widgets.impl.ITrackedColorProvider; + +import org.simantics.browsing.ui.swt.widgets.impl.ReadFactory; +import org.simantics.browsing.ui.swt.widgets.impl.Widget; +import org.simantics.browsing.ui.swt.widgets.impl.WidgetSupport; +import org.simantics.db.management.ISessionContext; +import org.simantics.db.procedure.Listener; + +/** + * Widget for choosing and labeled object from a set of objects. + * + * Supports cases when multiple objects have the same label (as much as possible) + * + * + * Based on org.simantics.browsing.ui.swt.widgets.Trackedtext + * + * + * @author Marko Luukkainen + * + */ +public class StringChooser implements Widget{ + private static final int EDITING = 1 << 0; + private static final int MODIFIED_DURING_EDITING = 1 << 1; + + /** + * Used to tell whether or not a mouseDown has occurred after a focusGained + * event to be able to select the whole text field when it is pressed for + * the first time while the widget holds focus. + */ + private static final int MOUSE_DOWN_FIRST_TIME = 1 << 2; + private static final int MOUSE_INSIDE_CONTROL = 1 << 3; + + private int caretPositionBeforeEdit; + + private String textBeforeEdit; + + private int state; + + private Map objectToLabel; + private Set allowedLabels; + + private T selected; + + private final Display display; + + private final Text text; + + private CompositeListener listener; + + private ListenerList modifyListeners; + + private ReadFactory objectFactory; + + private ITrackedColorProvider colorProvider; + + private final ResourceManager resourceManager; + + private boolean moveCaretAfterEdit = true; + + private boolean selectAllOnStartEdit = true; + + public StringChooser(Composite parent, WidgetSupport support, int style) { + this.state = 0; + this.text = new Text(parent, style); + this.display = text.getDisplay(); + this.resourceManager = new LocalResourceManager(JFaceResources.getResources(), text); + this.colorProvider = new DefaultColorProvider(resourceManager); + if (support!=null) support.register(this); + initialize(); + } + + + + public ResourceManager getResourceManager() { + return resourceManager; + } + + public void setFont(Font font) { + text.setFont(font); + } + + public void setObjectFactory(ReadFactory objectFactory) { + this.objectFactory = objectFactory; + } + + public void setMoveCaretAfterEdit(boolean value) { + this.moveCaretAfterEdit = value; + } + + public void setData(Map data) { + this.objectToLabel = data; + this.allowedLabels = new HashSet(); + this.allowedLabels.addAll(objectToLabel.values()); + } + + public void setData(Collection data) { + this.objectToLabel = new HashMap(); + this.allowedLabels = new HashSet(); + for (T t : data) { + String label = t.toString(); + objectToLabel.put(t, label); + allowedLabels.add(label); + } + } + + public void setSelected(T selected) { + if (selected != null) { + String label = objectToLabel.get(selected); + if (label == null) + return; + this.selected = selected; + this.text.setText(label); + } else { + this.selected = null; + this.text.setText(""); + } + } + + public void setSelected(String label) { + // TODO : we could create a label to object map. + for (T t : objectToLabel.keySet()) { + if (label.equals(objectToLabel.get(t))) { + setSelected(t); + return; + } + } + } + + + @Override + public void setInput(ISessionContext context, Object input) { + if (modifyListeners != null) { + for (Object o : modifyListeners.getListeners()) { + if(o instanceof Widget) { + ((Widget) o).setInput(context, input); + } + } + } + + if(objectFactory != null) { + objectFactory.listen(context, input, new Listener() { + + @Override + public void exception(final Throwable t) { + display.asyncExec(new Runnable() { + + @Override + public void run() { + if(isDisposed()) return; +// System.out.println("Button received new text: " + text); + text.setText(t.toString()); + } + + }); + } + + @Override + public void execute(final T object) { + + if(text.isDisposed()) return; + + display.asyncExec(new Runnable() { + + @Override + public void run() { + if(isDisposed()) return; + setSelected(object); +// text.getParent().layout(); +// text.getParent().getParent().layout(); + } + + }); + } + + @Override + public boolean isDisposed() { + return text.isDisposed(); + } + + }); + } + + } + + /** + * Common initialization. Assumes that text is already created. + */ + private void initialize() { + Assert.isNotNull(text); + + text.setBackground(colorProvider.getInactiveBackground()); + text.setDoubleClickEnabled(false); + + listener = new CompositeListener(); + + text.addModifyListener(listener); + text.addDisposeListener(listener); + text.addKeyListener(listener); + text.addMouseTrackListener(listener); + text.addMouseListener(listener); + text.addFocusListener(listener); + } + + public void startEdit(boolean selectAll) { + if (isEditing()) { + // Print some debug incase we end are in an invalid state + System.out.println("TrackedText: BUG: startEdit called when in editing state"); + } +// System.out.println("start edit: selectall=" + selectAll + ", text=" + text.getText() + ", caretpos=" + caretPositionBeforeEdit); + + // Backup text-field data for reverting purposes + caretPositionBeforeEdit = text.getCaretPosition(); + textBeforeEdit = text.getText(); + + // Signal editing state + setBackground(colorProvider.getEditingBackground()); + + if (selectAll) { + text.selectAll(); + } + state |= EDITING | MOUSE_DOWN_FIRST_TIME; + } + + private void applyEdit() { + try { + if (isTextValid() != null) { + text.setText(textBeforeEdit); + } else if (isModified() && !text.getText().equals(textBeforeEdit)) { + setSelected(text.getText()); + //System.out.println("apply"); + if (modifyListeners != null) { + StringChooserModifyEvent event = new StringChooserModifyEvent(text, selected, text.getText()); + for (Object o : modifyListeners.getListeners()) { + ((StringChooserModifyListener) o).modifySelection(event); + } + } + } + } catch (Throwable t) { + t.printStackTrace(); + } finally { + endEdit(); + } + } + + private void endEdit() { + if (text.isDisposed()) + return; + + if (!isEditing()) { + // Print some debug incase we end are in an invalid state + //ExceptionUtils.logError(new Exception("BUG: endEdit called when not in editing state")); + //System.out.println(); + } + setBackground(isMouseInsideControl() ? colorProvider.getHoverBackground() : colorProvider.getInactiveBackground()); +// System.out.println("endEdit: " + text.getText() + ", caret: " + text.getCaretLocation() + ", selection: " + text.getSelection()); + // Always move the caret to the end of the string + if(moveCaretAfterEdit) + text.setSelection(text.getCharCount()); + state &= ~(EDITING | MOUSE_DOWN_FIRST_TIME); + setModified(false); + } + + private void revertEdit() { + if (!isEditing()) { + // Print some debug incase we end are in an invalid state + //ExceptionUtils.logError(new Exception("BUG: revertEdit called when not in editing state")); + System.out.println("BUG: revertEdit called when not in editing state"); + } + text.setText(textBeforeEdit); + text.setSelection(caretPositionBeforeEdit); + setBackground(isMouseInsideControl() ? colorProvider.getHoverBackground() : colorProvider.getInactiveBackground()); + state &= ~(EDITING | MOUSE_DOWN_FIRST_TIME); + setModified(false); + } + + private boolean isEditing() { + return (state & EDITING) != 0; + } + + private void setModified(boolean modified) { + if (modified) { + state |= MODIFIED_DURING_EDITING; + } else { + state &= ~MODIFIED_DURING_EDITING; + } + } + + private boolean isMouseInsideControl() { + return (state & MOUSE_INSIDE_CONTROL) != 0; + } + + private void setMouseInsideControl(boolean inside) { + if (inside) + state |= MOUSE_INSIDE_CONTROL; + else + state &= ~MOUSE_INSIDE_CONTROL; + } + + private boolean isModified() { + return (state & MODIFIED_DURING_EDITING) != 0; + } + + public void setSelectAllOnStartEdit(boolean selectAll) { + this.selectAllOnStartEdit = selectAll; + } + + public void setEditable(boolean editable) { + if (editable) { + text.setEditable(true); + setBackground(isMouseInsideControl() ? colorProvider.getHoverBackground() : colorProvider.getInactiveBackground()); + } else { + text.setEditable(false); + text.setBackground(null); + } + } + + public void setText(String text) { + this.text.setText(text); + } + + public void setTextWithoutNotify(String text) { + this.text.removeModifyListener(listener); + setText(text); + this.text.addModifyListener(listener); + } + + public Text getWidget() { + return text; + } + + public synchronized void addModifyListener(StringChooserModifyListener listener) { + if (modifyListeners == null) { + modifyListeners = new ListenerList(ListenerList.IDENTITY); + } + modifyListeners.add(listener); + } + + public synchronized void removeModifyListener(StringChooserModifyListener listener) { + if (modifyListeners == null) + return; + modifyListeners.remove(listener); + } + + + + private String isTextValid() { + if (allowedLabels.contains(getWidget().getText())) + return null; + return "There is no such object."; + } + + public void setColorProvider(ITrackedColorProvider provider) { + Assert.isNotNull(provider); + this.colorProvider = provider; + } + + private void setBackground(Color background) { + if(text.isDisposed()) return; + if (!text.getEditable()) { + // Do not alter background when the widget is not editable. + return; + } + text.setBackground(background); + } + + public boolean isDisposed() { + return text.isDisposed(); + } + + public Display getDisplay() { + return display; + } + + public String getText() { + return text.getText(); + } + + public int getCaretPosition() { + return text.getCaretPosition(); + } + + + /** + * A composite of many UI listeners for creating the functionality of this + * class. + */ + private class CompositeListener + implements ModifyListener, DisposeListener, KeyListener, MouseTrackListener, + MouseListener, FocusListener + { + // Keyboard/editing events come in the following order: + // 1. keyPressed + // 2. verifyText + // 3. modifyText + // 4. keyReleased + + @Override + public void modifyText(ModifyEvent e) { + //System.out.println("modifyText: " + e); + setModified(true); + + String valid = isTextValid(); + if (valid != null) { + setBackground(colorProvider.getInvalidBackground()); + } else { + if (isEditing()) + setBackground(colorProvider.getEditingBackground()); + else + setBackground(colorProvider.getInactiveBackground()); + } + } + + @Override + public void widgetDisposed(DisposeEvent e) { + getWidget().removeModifyListener(this); + } + + private boolean isMultiLine() { + return (text.getStyle() & SWT.MULTI) != 0; + } + + private boolean hasMultiLineCommitModifier(KeyEvent e) { + return (e.stateMask & SWT.CTRL) != 0; + } + + @Override + public void keyPressed(KeyEvent e) { + //System.out.println("keyPressed: " + e); + if (!isEditing()) { + // ESC, ENTER & keypad ENTER must not start editing + if (e.keyCode == SWT.ESC) + return; + + if (!isMultiLine()) { + if (e.keyCode == SWT.F2 || e.keyCode == SWT.CR || e.keyCode == SWT.KEYPAD_CR) { + startEdit(selectAllOnStartEdit); + } else if (e.character != '\0') { + startEdit(false); + } + } else { + // In multi-line mode, TAB must not start editing! + if (e.keyCode == SWT.F2) { + startEdit(selectAllOnStartEdit); + } else if (e.keyCode == SWT.CR || e.keyCode == SWT.KEYPAD_CR) { + if (hasMultiLineCommitModifier(e)) { + e.doit = false; + } else { + startEdit(false); + } + } else if (e.keyCode == SWT.TAB) { + text.traverse(((e.stateMask & SWT.SHIFT) != 0) ? SWT.TRAVERSE_TAB_PREVIOUS : SWT.TRAVERSE_TAB_NEXT); + e.doit = false; + } else if (e.character != '\0') { + startEdit(false); + } + } + } else { + // ESC reverts any changes made during this edit + if (e.keyCode == SWT.ESC) { + revertEdit(); + } + if (!isMultiLine()) { + if (e.keyCode == SWT.CR || e.keyCode == SWT.KEYPAD_CR) { + applyEdit(); + } + } else { + if (e.keyCode == SWT.CR || e.keyCode == SWT.KEYPAD_CR) { + if (hasMultiLineCommitModifier(e)) { + applyEdit(); + e.doit = false; + } + } + } + } + } + + @Override + public void keyReleased(KeyEvent e) { + //System.out.println("keyReleased: " + e); + } + + @Override + public void mouseEnter(MouseEvent e) { + //System.out.println("mouseEnter"); + if (!isEditing()) { + setBackground(colorProvider.getHoverBackground()); + } + setMouseInsideControl(true); + } + + @Override + public void mouseExit(MouseEvent e) { + //System.out.println("mouseExit"); + if (!isEditing()) { + setBackground(colorProvider.getInactiveBackground()); + } + setMouseInsideControl(false); + } + + @Override + public void mouseHover(MouseEvent e) { + //System.out.println("mouseHover"); + setMouseInsideControl(true); + } + + @Override + public void mouseDoubleClick(MouseEvent e) { + //System.out.println("mouseDoubleClick: " + e); + if (e.button == 1) { + getWidget().selectAll(); + } + } + + @Override + public void mouseDown(MouseEvent e) { + //System.out.println("mouseDown: " + e); + if (!isEditing()) { + // In reality we should never get here, since focusGained + // always comes before mouseDown, but let's keep this + // fallback just to be safe. + if (e.button == 1) { + startEdit(selectAllOnStartEdit); + } + } else { + if (e.button == 1 && (state & MOUSE_DOWN_FIRST_TIME) != 0) { + if (!isMultiLine()) { + // This is useless for multi-line texts + getWidget().selectAll(); + } + state &= ~MOUSE_DOWN_FIRST_TIME; + } + } + } + + @Override + public void mouseUp(MouseEvent e) { + } + + @Override + public void focusGained(FocusEvent e) { + //System.out.println("focusGained"); + if (!isEditing()) { + if (!isMultiLine()) { + // Always start edit on single line texts when focus is gained + startEdit(selectAllOnStartEdit); + } + } + } + + @Override + public void focusLost(FocusEvent e) { + //System.out.println("focusLost"); + if (isEditing()) { + applyEdit(); + } + } + } +} diff --git a/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/StringChooserModifyEvent.java b/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/StringChooserModifyEvent.java new file mode 100644 index 00000000..c4916655 --- /dev/null +++ b/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/StringChooserModifyEvent.java @@ -0,0 +1,32 @@ +package org.simantics.jfreechart.chart.properties; + +import java.util.EventObject; + +import org.eclipse.swt.widgets.Widget; + +public class StringChooserModifyEvent extends EventObject { + + private static final long serialVersionUID = 2630732165074702762L; + + private T object; + private String text; + + public StringChooserModifyEvent(Widget source, T object, String text) { + super(source); + this.object = object; + this.text = text; + } + + public Widget getWidget() { + return (Widget) getSource(); + } + + public T getObject() { + return object; + } + + public String getText() { + return text; + } + +} diff --git a/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/StringChooserModifyListener.java b/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/StringChooserModifyListener.java new file mode 100644 index 00000000..b9756408 --- /dev/null +++ b/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/StringChooserModifyListener.java @@ -0,0 +1,7 @@ +package org.simantics.jfreechart.chart.properties; + +public interface StringChooserModifyListener { + + + void modifySelection(StringChooserModifyEvent e); +} diff --git a/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/StringChooserModifyListenerImpl.java b/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/StringChooserModifyListenerImpl.java new file mode 100644 index 00000000..8fa748dd --- /dev/null +++ b/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/StringChooserModifyListenerImpl.java @@ -0,0 +1,71 @@ +package org.simantics.jfreechart.chart.properties; + +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.swt.widgets.Text; +import org.simantics.browsing.ui.swt.widgets.impl.Widget; +import org.simantics.db.WriteGraph; +import org.simantics.db.common.request.WriteRequest; +import org.simantics.db.exception.DatabaseException; +import org.simantics.db.management.ISessionContext; +import org.simantics.utils.ReflectionUtils; +import org.simantics.utils.ui.ISelectionUtils; + +public abstract class StringChooserModifyListenerImpl implements StringChooserModifyListener, Widget { + protected ISessionContext context; + protected T lastInput = null; + + protected final Class clazz; + + public StringChooserModifyListenerImpl() { + clazz = ReflectionUtils.getSingleParameterType(getClass()); + } + + private Object getInputContents(Object input, Class inputClass) { + if (inputClass.isInstance(input)) + return input; + if (input instanceof ISelection) + return ISelectionUtils.filterSingleSelection(input, inputClass); + return null; + } + + @Override + public void modifySelection(StringChooserModifyEvent e) { + + Text text = (Text)e.getWidget(); + final T2 object = e.getObject(); + final String textValue = text.getText(); + final T input = lastInput; + + try { + context.getSession().syncRequest(new WriteRequest() { + + @SuppressWarnings("unchecked") + @Override + public void perform(WriteGraph graph) throws DatabaseException { + + if(clazz.isInstance(input)) { + applyObject(graph, (T)input, object, textValue); + } else { + T single = (T)getInputContents(input, clazz); + if(single != null) + applyObject(graph, single, object, textValue); + } + + + } + + }); + } catch (DatabaseException e1) { + e1.printStackTrace(); + } + } + + @Override + public void setInput(ISessionContext context, Object parameter) { + this.context = context; + lastInput = (T)parameter; + } + + abstract public void applyObject(WriteGraph graph, T input, T2 object, String text) throws DatabaseException; + +} diff --git a/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/VariableFactory.java b/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/VariableFactory.java deleted file mode 100644 index f9947090..00000000 --- a/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/VariableFactory.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.simantics.jfreechart.chart.properties; - -import org.simantics.browsing.ui.swt.widgets.impl.ReadFactoryImpl; -import org.simantics.db.ReadGraph; -import org.simantics.db.Resource; -import org.simantics.db.exception.DatabaseException; -import org.simantics.sysdyn.JFreeChartResource; -import org.simantics.utils.datastructures.BijectionMap; - -public class VariableFactory extends ReadFactoryImpl { - BijectionMap map = new BijectionMap(); - - public VariableFactory(BijectionMap map) { - this.map = map; - } - - @Override - public String perform(ReadGraph graph, Resource input) throws DatabaseException { - String value = graph.getPossibleRelatedValue(input, JFreeChartResource.getInstance(graph).variableRVI); - if (value == null) - return ""; - value = map.getRight(value); - if (value == null) - return ""; - return value; - - } - -} diff --git a/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/VariableProposalProvider.java b/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/VariableProposalProvider.java index 2f6f20f4..aa19d6de 100644 --- a/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/VariableProposalProvider.java +++ b/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/VariableProposalProvider.java @@ -13,8 +13,11 @@ package org.simantics.jfreechart.chart.properties; import java.util.ArrayList; import java.util.Collection; +import java.util.Iterator; -import org.eclipse.jface.fieldassist.SimpleContentProposalProvider; +import org.eclipse.jface.fieldassist.ContentProposal; +import org.eclipse.jface.fieldassist.IContentProposal; +import org.eclipse.jface.fieldassist.IContentProposalProvider; import org.eclipse.swt.widgets.Control; import org.simantics.browsing.ui.swt.widgets.impl.Widget; import org.simantics.browsing.ui.swt.widgets.impl.WidgetSupport; @@ -23,16 +26,112 @@ import org.simantics.db.management.ISessionContext; import org.simantics.db.procedure.Listener; import org.simantics.ui.SimanticsUI; import org.simantics.ui.utils.AdaptionUtils; -import org.simantics.utils.datastructures.Pair; /** - * Provides all variables a model contains * - * @author Teemu Lempinen + * @author Marko Luukkainen * */ -public class VariableProposalProvider extends IdLabelProposalProvider implements Widget { +public class VariableProposalProvider implements IContentProposalProvider, Widget { + /* + * The proposals provided. + */ + private Collection proposals; + + /* + * The proposals mapped to IContentProposal. Cached for speed in the case + * where filtering is not used. + */ + private IContentProposal[] contentProposals; + + /* + * Boolean that tracks whether filtering is used. + */ + private boolean filterProposals = false; + + + private boolean compareRVI = false; + /** + * Return an array of Objects representing the valid content proposals for a + * field. + * + * @param contents + * the current contents of the field (only consulted if filtering + * is set to true) + * @param position + * the current cursor position within the field (ignored) + * @return the array of Objects that represent valid proposals for the field + * given its current content. + */ + @SuppressWarnings("unchecked") + public IContentProposal[] getProposals(String contents, int position) { + if (filterProposals) { + ArrayList list = new ArrayList(); + if (compareRVI) { + for (ChartVariable proposal : proposals) { + if (proposal.getRvi().length() >= contents.length() && proposal.getRvi().substring(0, contents.length()).equalsIgnoreCase(contents)) { + if (proposal.getLabel() != null) + list.add(new ContentProposal(proposal.getRvi(),proposal.getLabel(), null)); + else + list.add(new ContentProposal(proposal.getRvi())); + } else if (proposal.getLabel() != null && proposal.getLabel().length() >= contents.length() && proposal.getLabel().substring(0, contents.length()).equalsIgnoreCase(contents)) { + list.add(new ContentProposal(proposal.getRvi(),proposal.getLabel(), null)); + } + } + } else { + for (ChartVariable proposal : proposals) { + if (proposal.getLabel() != null && proposal.getLabel().length() >= contents.length() && proposal.getLabel().substring(0, contents.length()).equalsIgnoreCase(contents)) { + list.add(new ContentProposal(proposal.getRvi(),proposal.getLabel(), null)); + } + } + } + + return (IContentProposal[]) list.toArray(new IContentProposal[list + .size()]); + } + if (contentProposals == null) { + contentProposals = new IContentProposal[proposals.size()]; + Iterator iter = proposals.iterator(); + for (int i = 0; i < proposals.size(); i++) { + ChartVariable proposal = iter.next(); + if (proposal.getLabel() != null) + contentProposals[i] = new ContentProposal(proposal.getRvi(),proposal.getLabel(),null); + else + contentProposals[i] = new ContentProposal(proposal.getRvi()); + } + } + return contentProposals; + } + + /** + * Set the Strings to be used as content proposals. + * + * @param items + * the array of Strings to be used as proposals. + */ + public void setProposals(Collection items) { + this.proposals = items; + contentProposals = null; + } + + /** + * Set the boolean that controls whether proposals are filtered according to + * the current field content. + * + * @param filterProposals + * true if the proposals should be filtered to + * show only those that match the current contents of the field, + * and false if the proposals should remain the + * same, ignoring the field content. + * @since 3.3 + */ + public void setFiltering(boolean filterProposals) { + this.filterProposals = filterProposals; + // Clear any cached proposals. + contentProposals = null; + } + /** * Provides all variables a model contains. Given resource needs to be * part of a model (i.e. using PartOf leads eventually to a SysdynModel). @@ -41,7 +140,7 @@ public class VariableProposalProvider extends IdLabelProposalProvider implements * @param resource A resource that is part of a model */ public VariableProposalProvider(final Control control, WidgetSupport support) { - super(new ArrayList());// super(String [] {}); + this.proposals = new ArrayList(); support.register(this); this.control = control; } diff --git a/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/bar/BarSeriesPropertyComposite2.java b/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/bar/BarSeriesPropertyComposite2.java index a53d122d..ec2f1dc9 100644 --- a/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/bar/BarSeriesPropertyComposite2.java +++ b/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/bar/BarSeriesPropertyComposite2.java @@ -24,20 +24,16 @@ import org.simantics.browsing.ui.swt.widgets.TrackedText; import org.simantics.browsing.ui.swt.widgets.impl.WidgetSupport; import org.simantics.db.management.ISessionContext; import org.simantics.jfreechart.chart.properties.ChartVariable; +import org.simantics.jfreechart.chart.properties.ChartVariableFactory; +import org.simantics.jfreechart.chart.properties.ChartVariableModifier; import org.simantics.jfreechart.chart.properties.DoubleValidator; import org.simantics.jfreechart.chart.properties.JFreeChartPropertyColorProvider; -import org.simantics.jfreechart.chart.properties.RVIFactory; -import org.simantics.jfreechart.chart.properties.RVIModifier; import org.simantics.jfreechart.chart.properties.RangeComposite; -import org.simantics.jfreechart.chart.properties.VariableExistsValidator; -import org.simantics.jfreechart.chart.properties.VariableFactory; -import org.simantics.jfreechart.chart.properties.VariableModifier; +import org.simantics.jfreechart.chart.properties.StringChooser; import org.simantics.layer0.Layer0; import org.simantics.modeling.ui.chart.property.DoublePropertyFactory; import org.simantics.modeling.ui.chart.property.DoublePropertyModifier; import org.simantics.sysdyn.JFreeChartResource; -import org.simantics.utils.datastructures.BijectionMap; -import org.simantics.utils.datastructures.Pair; /** * Composite for modifying properties of a series in a bar chart @@ -46,7 +42,8 @@ import org.simantics.utils.datastructures.Pair; */ public class BarSeriesPropertyComposite2 extends Composite { - private TrackedText variable, label, time; + private TrackedText label, time; + private StringChooser variable; public BarSeriesPropertyComposite2(Composite parent, final ISessionContext context, WidgetSupport support, Collection variables, int style) { super(parent, style); @@ -58,17 +55,23 @@ public class BarSeriesPropertyComposite2 extends Composite { label.setText("Variable:"); GridDataFactory.fillDefaults().align(SWT.END, SWT.FILL).applyTo(label); - variable = new TrackedText(this, support, SWT.BORDER); - - // FIXME: using bijectionmap and trackedText looses the variables that have the same label. - BijectionMap map = new BijectionMap(); - for (ChartVariable variable : variables) { - map.map(variable.getRvi(), variable.toString()); - } - variable.setTextFactory(new VariableFactory(map)); - variable.addModifyListener(new VariableModifier(variable.getWidget(), support)); - variable.setInputValidator(new VariableExistsValidator(support, variable, false, true)); +// variable = new TrackedText(this, support, SWT.BORDER); +// +// // FIXME: using bijectionmap and trackedText looses the variables that have the same label. +// BijectionMap map = new BijectionMap(); +// for (ChartVariable variable : variables) { +// map.map(variable.getRvi(), variable.toString()); +// } +// variable.setTextFactory(new VariableFactory(map)); +// variable.addModifyListener(new VariableModifier(variable.getWidget(), support)); +// variable.setInputValidator(new VariableExistsValidator(support, variable, false, true)); + + variable = new StringChooser(this, support, SWT.BORDER); + variable.setData(variables); + variable.setObjectFactory(new ChartVariableFactory(variables)); + variable.addModifyListener(new ChartVariableModifier(variable.getWidget(), support)); + variable.setColorProvider(new JFreeChartPropertyColorProvider(this.variable.getResourceManager())); GridDataFactory.fillDefaults().grab(true, false).applyTo(this.variable.getWidget()); diff --git a/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/pie/PieSeriesPropertyComposite2.java b/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/pie/PieSeriesPropertyComposite2.java index e0c3d891..70d0076e 100644 --- a/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/pie/PieSeriesPropertyComposite2.java +++ b/org.simantics.jfreechart/src/org/simantics/jfreechart/chart/properties/pie/PieSeriesPropertyComposite2.java @@ -27,21 +27,17 @@ import org.simantics.db.management.ISessionContext; import org.simantics.jfreechart.chart.properties.BooleanPropertyFactory; import org.simantics.jfreechart.chart.properties.BooleanSelectionListener; import org.simantics.jfreechart.chart.properties.ChartVariable; +import org.simantics.jfreechart.chart.properties.ChartVariableFactory; +import org.simantics.jfreechart.chart.properties.ChartVariableModifier; import org.simantics.jfreechart.chart.properties.ColorPicker; import org.simantics.jfreechart.chart.properties.DoubleValidator; import org.simantics.jfreechart.chart.properties.JFreeChartPropertyColorProvider; -import org.simantics.jfreechart.chart.properties.RVIFactory; -import org.simantics.jfreechart.chart.properties.RVIModifier; import org.simantics.jfreechart.chart.properties.RangeComposite; -import org.simantics.jfreechart.chart.properties.VariableExistsValidator; -import org.simantics.jfreechart.chart.properties.VariableFactory; -import org.simantics.jfreechart.chart.properties.VariableModifier; +import org.simantics.jfreechart.chart.properties.StringChooser; import org.simantics.layer0.Layer0; import org.simantics.modeling.ui.chart.property.DoublePropertyFactory; import org.simantics.modeling.ui.chart.property.DoublePropertyModifier; import org.simantics.sysdyn.JFreeChartResource; -import org.simantics.utils.datastructures.BijectionMap; -import org.simantics.utils.datastructures.Pair; /** * Composite containing the properties of a series @@ -50,7 +46,8 @@ import org.simantics.utils.datastructures.Pair; */ public class PieSeriesPropertyComposite2 extends Composite { - private TrackedText variable, label, time; + private TrackedText label, time; + private StringChooser variable; public PieSeriesPropertyComposite2(Composite parent, ISessionContext context, WidgetSupport support, Collection variables,int style) { super(parent, style); @@ -62,14 +59,11 @@ public class PieSeriesPropertyComposite2 extends Composite { label.setText("Variable:"); GridDataFactory.fillDefaults().align(SWT.END, SWT.FILL).applyTo(label); - variable = new TrackedText(this, support, SWT.BORDER); - BijectionMap map = new BijectionMap(); - for (ChartVariable variable : variables) { - map.map(variable.getRvi(), variable.toString()); - } - variable.setTextFactory(new VariableFactory(map)); - variable.addModifyListener(new VariableModifier(variable.getWidget(), support)); - variable.setInputValidator(new VariableExistsValidator(support, variable, false, true)); + + variable = new StringChooser(this, support, SWT.BORDER); + variable.setData(variables); + variable.setObjectFactory(new ChartVariableFactory(variables)); + variable.addModifyListener(new ChartVariableModifier(variable.getWidget(), support)); variable.setColorProvider(new JFreeChartPropertyColorProvider(this.variable.getResourceManager())); GridDataFactory.fillDefaults().grab(true, false).applyTo(this.variable.getWidget()); -- 2.47.1