All Downloads are FREE. Search and download functionalities are using the official Maven repository.

at.spardat.xma.mdl.list.ListDomUIDelegateClient Maven / Gradle / Ivy

/*******************************************************************************
 * Copyright (c) 2003, 2007 s IT Solutions AT Spardat GmbH .
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     s IT Solutions AT Spardat GmbH - initial API and implementation
 *******************************************************************************/

// @(#) $Id: ListDomUIDelegateClient.java 7135 2011-01-27 00:28:07Z hoenninger $
package at.spardat.xma.mdl.list;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Locale;
import java.util.ResourceBundle;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.TypedEvent;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;

import at.spardat.enterprise.fmt.FmtParseException;
import at.spardat.xma.boot.logger.LogLevel;
import at.spardat.xma.component.ComponentClient;
import at.spardat.xma.datasource.IDomRow;
import at.spardat.xma.datasource.ITabularDataSource;
import at.spardat.xma.datasource.ITabularDomData;
import at.spardat.xma.mdl.AttachmentExceptionClient;
import at.spardat.xma.mdl.ModelChangeEvent;
import at.spardat.xma.mdl.UIDelegateClient;
import at.spardat.xma.mdl.ValidationErrorClient;
import at.spardat.xma.mdl.WModel;
import at.spardat.xma.page.EventAdapter;
import at.spardat.xma.page.PageClient;
import at.spardat.xma.plugins.PluginManager;
import at.spardat.xma.security.XMAContext;
import at.spardat.xma.session.XMASession;
import at.spardat.xma.session.XMASessionClient;


/**
 * This class is the UIDelegate for a ListDomWMClient widget model. It
 * manages the interaction between a SWT-DROP_DOWN-combo and the widget model.
 * The attached Combo must be created with the style SWT.DROP_DOWN. The style
 * SWT.READ_ONLY must not be set. 

* * Technical footnote#1: A Combo that is not read-only has two states: its text * (in the text field) and the selectionIndex of the drop-down-list. If the * selectionIndex is set (differs from -1), the text-field is displayed with * selected contents if the shell is resized in the current version of SWT. * Therefore, this class never sets the selectionIndex, but just the text. * On loose-focus, a selection index, if any, is cleared and the text is set. * * @author YSD, 20.04.2003 19:06:19 */ public class ListDomUIDelegateClient extends UIDelegateClient { /** * The attached SWT-control. */ private Combo combo_; /** * reacts on mouse moves on combo_ */ private MouseMoveListener mouseMoveListenerOnCombo_; /** * The opional label usually positioned at the left hand side of the combo. */ private Label label_; /** * The widget model. */ private ListDomWMClient wModel_; /** * Keep track of whether we are updating the UI. */ private boolean updatingUI_ = false; /** * Holds all valid DomValue objects in the right sort order. */ private ArrayList lstValues_; /** * Is non null if the first entry in the combo is a key that is * not in the domain of values itself. This may happen, if the * selection in the model is out of range. This variable * then holds the key of this out of domain selection. */ private String outOfDomainKey_; /** * Holds all outdated DomValue objects. */ private ArrayList lstOutDatedValues_; /** * The editable-property */ private boolean editable_ = true; /** * The enabled-property */ private boolean enabled_ = true; /** * Stores the drop down lists visibleItemCount. * If a combo model is set to setEditable(false) then the widgets.setVisibleItemCount() is set to 0 * and the old value is stored in this property for resetting the widget's value at setEditable(true). */ private int visibleItemCount_; /** * Constructor */ public ListDomUIDelegateClient (ListDomWMClient wModel) { wModel_ = wModel; } /** * The following events must be notified from the SWT combo box: *

    *
  1. SelectionEvent *
  2. ModifyEvent *
  3. FocusEvent for lost focus *
* * @see at.spardat.xma.mdl.UIDelegateClient#handleUIEvent(java.lang.Object, int) */ public void handleUIEvent (Object event, int type) { if (!isUIAttached()) return; if (updatingUI_) { return; } // sanity check: the Widget in the event must be control_ if (event instanceof TypedEvent) { if (((TypedEvent)event).widget != combo_) { System.err.println ("widget in event differs from control in UIDelegate; event ignored ..."); return; } } updatingUI_ = true; try { if (!editable_) { /** * In the case where the combo is not editable, we nevertheless get events since we cannot * avoid it. But we ignore it and restore the combo to the previous state. */ selection2UI(); } else if (event instanceof ModifyEvent) { /** * the user modified the text field. This results in a ModifyEvent where * Combo.getSelectionIndex yields -1. In this case, we do a praefix search * and update the text to the best matching entry. */ if (combo_.getSelectionIndex() == -1) { selectLongestCommonPraefix(); } } else if (event instanceof SelectionEvent) { if (combo_.getSelectionIndex() != -1) { /** * the user selected an entry in the combo box list. Here we update the model. */ String key = comboSelIndex2Key(combo_.getSelectionIndex()); wModel_.handle(wModel_.new SelectionChangeEvent(key, true)); } } else if (event instanceof FocusEvent) { if (combo_.getSelectionIndex() != -1) { /** * removes also selection from the combo box, see footnote#1 */ selection2UI(); } } } finally { updatingUI_ = false; updateErrorState(); } } /** * @see at.spardat.xma.mdl.UIDelegateClient#handleModelChangeEvent(at.spardat.xma.mdl.ModelChangeEvent) */ public void handleModelChangeEvent (ModelChangeEvent event) { // if the UI is not attached, do nothing if (!isUIAttached()) return; updatingUI_ = true; try { if (event instanceof ListDomWM.SelectionChangedEvent || event instanceof ListDomWM.SelectionChangeEvent) { selection2UI(); } else if (event instanceof ListDomWM.DataSourceChangedEvent) { // read values lstValues_ = null; ensureDataSourceRead(); // fill combo domain2UI(); // selection selection2UI(); } else if (event instanceof ListDomWMClient.MandatoryChangedEvent) { // the mandatory property changed // here it suffices to call updateErrorState, which is done anyway below. } } finally { updatingUI_ = false; updateErrorState(); } } /** * @see at.spardat.xma.mdl.UIDelegateClient#getWModel() */ public WModel getWModel () { return wModel_; } // see at.spardat.xma.mdl.UIDelegateClient.createControl() public Object createControl(Object parent) { if(!(parent instanceof Composite)) throw new IllegalArgumentException("parent must be a composite"); return new Combo(((Composite)parent), SWT.DROP_DOWN|SWT.BORDER); } // see at.spardat.xma.mdl.UIDelegateClient.addListeners() public void addListeners(Object control, EventAdapter adapter) { if(control instanceof Combo) { ((Combo)control).addFocusListener(adapter); ((Combo)control).addModifyListener(adapter); ((Combo)control).addSelectionListener(adapter); } else throw new IllegalArgumentException("unsupported control type: "+control.getClass().getName()); } /** * @see at.spardat.xma.mdl.UIDelegateClient#isUIAttached() */ public boolean isUIAttached () { return combo_ != null; } /** * @see at.spardat.xma.mdl.UIDelegateClient#attachUI(java.lang.Object, java.lang.Object) */ public void attachUI (Object control, Object label) throws AttachmentExceptionClient { if (isUIAttached()) throw new IllegalStateException(); boolean attach = false; if (control instanceof Combo) { Combo co = (Combo)control; int style = co.getStyle(); // combo must have the style SWT.DROP_DOWN and must not have the style SWT.READ_ONLY if ((style & SWT.DROP_DOWN) != 0 && (style & SWT.READ_ONLY) == 0) { attach = true; } combo_ = co; if (wModel_.isShowLongValueAsTooltips_()) { // add mousemovelistener on combo mouseMoveListenerOnCombo_ = new MouseMoveListener () { public void mouseMove(MouseEvent e) { adjustToolTip(); } }; combo_.addMouseMoveListener(mouseMoveListenerOnCombo_); } } if (!attach) { throw new AttachmentExceptionClient ("ListDomWModel must be attached to a Combo with style SWT.DROP_DOWN and not SWT.READ_ONLY"); } updatingUI_ = true; try { // read values ensureDataSourceRead(); // fill combo domain2UI(); // selection selection2UI(); // save label if (label != null && label instanceof Label) { label_ = (Label) label; } // reflect editable state on the UI setEditable (editable_); // just to set the background color //if the control is already disabled, set this at enabled_ - otherwise do not change enabled_ //as in the Gen class the GUI designer disable property is set directly at the widget (only for some models) enabled_ = combo_.getEnabled()?enabled_:false; setEnabled(enabled_); //set the model's enabled state at the control } finally { updatingUI_ = false; updateErrorState(); } } /** * @see at.spardat.xma.mdl.UIDelegateClient#getUIControl() */ public Object getUIControl () { if (!isUIAttached()) throw new IllegalStateException (); return combo_; } /** * @see at.spardat.xma.mdl.UIDelegateClient#getUILabel() */ public Object getUILabel() { if (!isUIAttached()) throw new IllegalStateException (); return label_; } /** * @see at.spardat.xma.mdl.UIDelegateClient#detachUI() */ public void detachUI () { updatingUI_ = true; try { // clear a previous error state PageClient pageModel = wModel_.getPageModelC(); pageModel.clearValidationErrorImpl (combo_); pageModel.clearError(combo_); pageModel.getDialogPage().clearWarning(combo_); pageModel.getDialogPage().clearInfo(combo_); if (mouseMoveListenerOnCombo_ != null) { combo_.removeMouseMoveListener(mouseMoveListenerOnCombo_); mouseMoveListenerOnCombo_ = null; } enabled_ = combo_.getEnabled(); combo_ = null; label_ = null; outOfDomainKey_ = null; } finally { updatingUI_ = false; } } /** * Reads the data source and fills the ArrayList lstValues_. This method * does nothing if the instance variable lstValues_ is not equal to null. * The values are also sorted according to the order style of the Widget Model.

* * Since data is read from a data source, internally, there may abide a bunch of * exceptions. But this method is deep in the call hierarchy and may also * be called while synchronizing changes from the server, exceptions are not * thrown at the moment. The combo box is empty then in the case of failures. */ private void ensureDataSourceRead () { if (lstValues_ != null) return; lstValues_ = new ArrayList(); lstOutDatedValues_ = new ArrayList(); String dsSpec = wModel_.getDataSource(); // may be null if (dsSpec != null && dsSpec.length() > 0) { /** * there is a non-empty data source specification */ XMASession session = ((PageClient)wModel_.getPage()).getComponent().getSession(); PluginManager piManager = session.getPluginManager(); ITabularDataSource dataSourcePlugin = (ITabularDataSource) piManager.getPlugin(ITabularDataSource.class); // find component Composite parent = combo_.getParent(); PageClient page = (PageClient) parent.getData(); while (page==null && parent!=null) { parent = parent.getParent(); page = (PageClient) parent.getData(); } ITabularDomData data = null; try { data = dataSourcePlugin.getDomTable(dsSpec, page.getComponent().getSession()); int size = data.size(); for (int i=0; ilstValues_. */ private void domain2UI () { combo_.removeAll(); outOfDomainKey_ = null; for (int i=0; i=0; i--) { DomValue value = (DomValue)lstOutDatedValues_.get(i); if (value.key_.equals(modelKey)) return value; } return null; } /** * This method updates the UI to reflect the selection state of the Widget Model. * It requires that the domain combo is filled with values and that the data * is read from the data source. */ protected void selection2UI () { combo_.deselectAll(); // never allow entries to be selected, see footnote#1 String selected = wModel_.getSelected(); if (selected == null) { // clear selection of the combo combo_.setText(""); } else { // selected is here int indexInValues = indexOfKey (selected); if (indexInValues != -1) { // a valid selection where the selected key is part of the domain int indexInCombo = outOfDomainKey_ == null ? indexInValues : indexInValues + 1; // combo_.select(indexInCombo); uncommented because of footnote#1 DomValue domVal = (DomValue)lstValues_.get(indexInValues); combo_.setText(domVal2ComboLine(domVal)); } else { String useOutOfDomain = wModel_.getPageModelC().getComponent().getSession().getRuntimeProperty("useOutOfDomainKey","true"); if("true".equalsIgnoreCase(useOutOfDomain)) { // a selection where the value is not part of the domain // check if an outOfDomain key is already included in the combo if (outOfDomainKey_ != null) { // first remove the old one combo_.remove(0); outOfDomainKey_ = null; } // add a new one to the combo combo_.add(unknown2ComboLine(selected), 0); outOfDomainKey_ = selected; // select it //combo_.select(0); // uncommented because of footnote#1 combo_.setText(unknown2ComboLine(selected)); } else { // if outOfDomainKey should not be used // only show the value in the text field, but not in the drop down box. DomValue domVal = getOutdated(selected); if(domVal!=null) { combo_.setText(domVal2ComboLine(domVal)); } else { combo_.setText(unknown2ComboLine(selected)); } } } } } /** * Changes the tooltip on every change in this */ private void adjustToolTip () { /** * change the tooltip to the longvalue if the corresponding flag is set on the model */ if (wModel_.isShowLongValueAsTooltips_()) { String selectedLongValue = null; String selectedKey = wModel_.getSelected(); if (selectedKey != null) { int indexInValues = indexOfKey (selectedKey); if (indexInValues != -1) { selectedLongValue = ((DomValue)lstValues_.get(indexInValues)).longVal_; } } final String selectedLongValueF = selectedLongValue; final String oldText = combo_.getToolTipText(); if (!strEqu (oldText, selectedLongValueF)) { combo_.setToolTipText (selectedLongValueF); } } } // are the two strings equal; if both Strings are null, they are considered to be equal private boolean strEqu (String s1, String s2) { if (s1 == null || s2 == null) return s1 == null && s2 == null; else return s1.equals (s2); } /** * Yields true, if and only if the long value is shown in the combo. */ private boolean showLong () { return (wModel_.getShowStyle() & ListDomWMClient.SHOW_LONG) != 0; } /** * Yields true, if and only if the short value is shown in the combo. */ private boolean showJustShort () { return (wModel_.getShowStyle() & ListDomWMClient.SHOW_SHORT) != 0; } /** * Searches lstValues_ for a particular key and returns the index if found * or -1 if not found. */ private int indexOfKey (String key) { for (int i=lstValues_.size()-1; i>=0; i--) { if (((DomValue)lstValues_.get(i)).key_.equals(key)) return i; } return -1; } /** * Retrieves the text of the combo boxes text field and searches for an entry * in the combo box whose string has a maximum common praefix with the text. * If found, this entry is selected. Otherwise, the text is cleared. * The widget model is also kept up to date. */ private void selectLongestCommonPraefix () { int indexMaxCommon = -1; int maxCommon = 0; String comboText = combo_.getText(); // stripp off the part after the caret int newLen = Math.min(combo_.getSelection().y, comboText.length()); comboText = comboText.substring(0, newLen); if (showLong()) { // the algorithm if long values are displayed // here we perform the search on the contents of the combo box for (int i=0; i maxCommon) { maxCommon = common; indexMaxCommon = i; } } } else { // the algorithm if short (and optionally long) values are displayed // here we search the ArrayList lstValues for (int i=0; i maxCommon) { maxCommon = common; indexMaxCommon = i; } } // convert the index from a lstValues-index to a combolist index if (outOfDomainKey_ != null) indexMaxCommon++; // check for a match with the outOfDomainKey if (outOfDomainKey_ != null) { int common = commonPraefixLen (comboText, outOfDomainKey_); if (common == comboText.length() && common == outOfDomainKey_.length() // exact match || common > maxCommon) { // better match maxCommon = common; indexMaxCommon = 0; } } } if (maxCommon == 0) { // nothing in common --> deselect wModel_.handle(wModel_.new SelectionChangeEvent(null, true)); selection2UI(); } else { // the maximum common praefix of len maxCommon is at list index indexMaxCommon // select this entry String key = comboSelIndex2Key (indexMaxCommon); DomValue dv = comboSelIndex2DomValue (indexMaxCommon); wModel_.handle(wModel_.new SelectionChangeEvent(key, true)); selection2UI(); // correct cursor int point = maxCommon; if (!showLong()) { // when showing the concatination (shortValue - longValue), restrict the caret to the // the length of the short value if (dv == null) { point = Math.min (point, outOfDomainKey_.length()); } else { point = Math.min (point, dv.shortVal_.length()); } } combo_.setSelection(new Point (point, point)); } } /** * Expects a selection index of the combo boxes list and maps this to a key * in the domain. */ private String comboSelIndex2Key (int index) { DomValue dv = comboSelIndex2DomValue (index); if (dv == null) return outOfDomainKey_; return dv.key_; } /** * Expects a selection index of the combo box and maps it to a DomValue object * * @param index index returned by combo_.getSelectionIndex() * @return DomValue object or null if the selection is the outOfDomainKey_ */ private DomValue comboSelIndex2DomValue (int index) { if (outOfDomainKey_ != null && index == 0) { // the key is the outOfDomainKey return null; } else { // an ordinary entry which is also part of the domain if (outOfDomainKey_ != null) index--; return (DomValue)(lstValues_.get(index)); } } /** * Returns the length of the common praefix of two Strings, that is the number * of equal leading characters. * * @param s1 the first string. Must not be null. * @param s2 the second string. Must not be null. * @return number of equal characters at the front of the two strings. */ private int commonPraefixLen (String s1, String s2) { int len = Math.min(s1.length(), s2.length()); int result = len; for (int i=0; iinError, this method sets the background of the corresponding Control c * to the error color defined in ComponentClient. If inError is false, * the background is reset to the default color. */ public void setErrorColor (boolean inError) { if (editable_) { if(inError) { combo_.setBackground (wModel_.getPageModelC().getComponent().getColor(ComponentClient.errorBackground)); } else { combo_.setBackground(null); } } else { combo_.setBackground (Display.getCurrent().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND)); } } /** * Returns the text of the label which may be null, if the label is unknown. */ private String getLabelText () { String text = label_ != null ? label_.getText() : null; return text; } /** * @see at.spardat.xma.mdl.UIDelegateClient#isEnabled() */ public boolean isEnabled () { if (isUIAttached()) { //always take the state from the widget (if it was set directly on it) as this is the old behavior enabled_ = combo_.isEnabled(); } return enabled_; } /** * @see at.spardat.xma.mdl.UIDelegateClient#setEnabled(boolean) */ public void setEnabled (boolean what) { enabled_ = what; if (isUIAttached()) combo_.setEnabled(what); } /** * @see at.spardat.xma.mdl.UIDelegateClient#isEditable() */ public boolean isEditable () { return editable_; } /** * @see at.spardat.xma.mdl.UIDelegateClient#setEditable(boolean) */ public void setEditable (boolean what) { editable_ = what; if (isUIAttached()) { updateErrorState(); int count = combo_.getVisibleItemCount(); if(count>0){ visibleItemCount_ = count; } if(!editable_){ combo_.setVisibleItemCount(0); }else { combo_.setVisibleItemCount(visibleItemCount_); } } } /** * Models a single domain value to be displayed in the combo box. * * @author YSD, 22.04.2003 11:17:34 */ private static class DomValue { // the key; never null public String key_; // the short value; never null public String shortVal_; // the long value; never null public String longVal_; } /** * Comparator to sort the list of DomValues according to the ORDER_-styles of * class ListDomWMClient. * * @author YSD, 22.04.2003 13:11:58 */ private static class DomValueComparator implements Comparator { /** * Constructs a Comparator to sort the list of DomValues * * @param orderStyle Order style of class ListDomWMClient */ public DomValueComparator (int orderStyle) { orderStyle_ = orderStyle; } /** * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) */ public int compare (Object o1, Object o2) { DomValue v1 = (DomValue) o1; DomValue v2 = (DomValue) o2; if ((orderStyle_ & ListDomWMClient.ORDER_LONG) != 0) { return v1.longVal_.compareTo(v2.longVal_); } else { return v1.shortVal_.compareTo(v2.shortVal_); } } private int orderStyle_; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy