/* * � Copyright IBM Corp. 2011 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. See the License for the specific language governing * permissions and limitations under the License. */ package com.ibm.xsp.extlib.designer.tooling.propeditor; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.StringTokenizer; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.ScrolledComposite; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import com.ibm.commons.iloader.node.validators.IntegerValidator; import com.ibm.commons.swt.SWTUtils; import com.ibm.commons.swt.controls.custom.CustomCheckBox; import com.ibm.commons.swt.data.dialog.LWPDNoTitleAreaDialog; import com.ibm.commons.swt.data.editors.api.AbstractTextEditor; import com.ibm.commons.swt.data.editors.api.CompositeEditor; import com.ibm.commons.util.StringUtil; /** * * This property editor takes a parameter which is a set of value-label pairs. * The values must be specified in HEX (like 0x001 for 1 etc...) * This editor will then pop up a dialog which will contain a checkBox for each value-label pair * Once the user has checked a number of the checkBoxes, the values for each of the checked checkBoxes * are OR'd together and the result is returned as an int. * * The parameters can be specified either separated by line delimiters or commas. Like this * <editor-parameter> * <value>:<label>,<value>:<label>,..... * </editor-parameter> * or * <editor-parameter> * <value>:<label> * <value>:<label> * ..... * </editor-parameter> * * Here is a real example * <editor-parameter> * 0x001:Entries * 0x002:Top Level * </editor-parameter> */ public class BitwiseFlagsPicker extends AbstractTextEditor { //The parameters defined for this picker private String _parameters; //This is the title of the dialog we pop up to choose the options private final String DIALOG_TITLE = "Select Flags"; // $NLX-BitwiseFlagsPicker.SelectFlags-1$ /** * Default constructor */ public BitwiseFlagsPicker() { super(); } /** * Constructor called with parameters specified for the property editor. * @param parameters */ public BitwiseFlagsPicker(String parameters) { super(); _parameters = parameters; //add a validator to the field, so that you can only add an int value directly into the all props panel addValidator(IntegerValidator.instance); } /* * (non-Javadoc) * @see com.ibm.commons.swt.data.editors.api.PropertyEditor#hasDialogButton(com.ibm.commons.swt.data.editors.api.CompositeEditor) */ @Override public boolean hasDialogButton(CompositeEditor parent) { return true; } /** * This method parses the parameters string into a usable map of values to labels. * @return */ private HashMap<ComparableHexString, String> parseParameters(){ HashMap<ComparableHexString, String> valueLabelPairs = new HashMap<ComparableHexString, String>(); //we allow parameters separated by commas or carriage returns, so those are our tokens. String tokens = ",\r\n"; // $NON-NLS-1$ StringTokenizer st = new StringTokenizer(_parameters, tokens); // $NON-NLS-1$ while (st.hasMoreTokens()) { String line = st.nextToken().trim(); if(StringUtil.isNotEmpty(line)) { String value; String label; //prefix and tagName are separated by colons. int pos = line.indexOf(':'); if(pos>=0) { value = line.substring(0,pos); label = line.substring(pos+1); valueLabelPairs.put(new ComparableHexString(value),label); } } } if(valueLabelPairs.size()>0){ return valueLabelPairs; } return null; } /** * This class allows us to sort the parameters by the hex bit flag */ private class ComparableHexString implements Comparable<ComparableHexString>{ private String _hexString; //default to -1 in case intValue cannot be resolved from hex value. private int _intValue = -1; /** * Standard constructor to create hex strings that can be compared with each other. * @param hexString */ public ComparableHexString(String hexString){ if(StringUtil.isNotEmpty(hexString)){ _hexString = hexString; try{ int intValue = Integer.decode(hexString); if(intValue >= 0){ _intValue = intValue; } } catch(NumberFormatException nfe){ //do nothing in this case. } } } /* * (non-Javadoc) * @see java.lang.Comparable#compareTo(java.lang.Object) */ public int compareTo(ComparableHexString anotherHexString) { if(null != anotherHexString){ if(this.getIntValue()>anotherHexString.getIntValue()){ //our hex value is bigger than the other one, so we should be further down the list return 1; } else if(this.getIntValue()<anotherHexString.getIntValue()){ //our hex value is smaller than the other one, so we should be higher up the list return -1; } } //should not get to this case, but if we do, consider the values equal. return 0; } /** * Get the hex string as it was passed in * @return */ public String getHexString(){ return _hexString; } /** * Get the int value of the hex string that was passed in * @return */ public int getIntValue(){ return _intValue; } } /* * (non-Javadoc) * @see com.ibm.commons.swt.data.editors.api.PropertyEditor#callDialog(com.ibm.commons.swt.data.editors.api.CompositeEditor, java.lang.String) */ @Override public String callDialog(CompositeEditor parent, String value) { BitPickerDialog dialog = new BitPickerDialog(parent, value); int result = dialog.open(); if (result == IDialogConstants.OK_ID) { return dialog.getValue(); } return value; } /** * This class is the Dialog we pop up to allow users to select options defined in the parameters of this editor. */ private class BitPickerDialog extends LWPDNoTitleAreaDialog { //initial height and width of the dialog when it is popped up private int INITIAL_DIALOG_HEIGHT = 137; private int INITIAL_DIALOG_WIDTH = 167; //initial height and width of the scrolled composite containing the checkBoxes in the dialog private int SCROLLED_COMPOSITE_HEIGHT = 130; private int SCROLLED_COMPOSITE_WIDTH = 190; //the OR'd values of all the selected checkBoxes in the dialog. private String _value; private ArrayList<ExtendedCustomCheckbox> _optionCheckBoxes = new ArrayList<ExtendedCustomCheckbox>(); public BitPickerDialog(CompositeEditor parent, String value){ super(parent.getShell()); _value = value; } /* * (non-Javadoc) * @see com.ibm.commons.swt.data.dialog.LWPDCommonDialog#fillClientArea(org.eclipse.swt.widgets.Composite) */ @Override protected void fillClientArea(Composite parent) { //create the scrolled area that will contain the checkBox container composite. ScrolledComposite scrolledParentComposite = createScrolledComposite(parent); //create the composite that will hold the checkBoxes and become the content of the scrolled composite Composite checkBoxContainerComposite = new Composite(scrolledParentComposite, SWT.NONE); // $NON-NLS-1$ checkBoxContainerComposite.setLayout(new GridLayout()); checkBoxContainerComposite.setLayoutData(new GridData(GridData.FILL_BOTH)); checkBoxContainerComposite.setBackground(parent.getDisplay().getSystemColor(SWT.COLOR_WHITE)); //parse the parameters to get a HashMap of value label pairs, that we use to create checkBoxes. HashMap<ComparableHexString,String> valueLabelPairs = parseParameters(); if(!valueLabelPairs.isEmpty()){ ArrayList<ComparableHexString> values = new ArrayList<ComparableHexString>(valueLabelPairs.keySet()); Collections.sort(values); Iterator<ComparableHexString> valuesIter = values.iterator(); while(valuesIter.hasNext()){ //double check that value is in fact an int that we can do bitwise operation on. ComparableHexString value = valuesIter.next(); if(value.getIntValue() > -1){ String label = valueLabelPairs.get(value); if(StringUtil.isNotEmpty(label)){ //create the checkBox that will set this bit flag and add it to our list of checkBoxes. ExtendedCustomCheckbox checkBox = createCheckBox(checkBoxContainerComposite, label, value.getHexString()); _optionCheckBoxes.add(checkBox); } } } //if there was already a value set then we need to see which checkBoxes should be checked based on that value. updateCheckboxState(); //set the checkBox container composite as the content of the scrolled composite scrolledParentComposite.setContent(checkBoxContainerComposite); scrolledParentComposite.setMinSize(checkBoxContainerComposite.computeSize(SWT.DEFAULT, SWT.DEFAULT)); } } /** * Create the scrolled area of the dialog that will contain the checkBoxes. * @param parent * @return */ private ScrolledComposite createScrolledComposite(Composite parent){ ScrolledComposite composite = new ScrolledComposite(parent, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL); composite.setLayout(new GridLayout()); GridData data = new GridData(SWT.BEGINNING, SWT.BEGINNING, false, false); data.widthHint = SCROLLED_COMPOSITE_WIDTH; data.heightHint = SCROLLED_COMPOSITE_HEIGHT; data.horizontalSpan = 2; composite.setLayoutData(data); composite.setExpandHorizontal(true); composite.setExpandVertical(true); return composite; } /** * Create the checkBox control that represents one of the bit flags. * @param parent - the parent composite of the checkBox * @param label - the display label for the checBox * @param value - the selected value of the checkBox * @return */ private ExtendedCustomCheckbox createCheckBox(Composite parent, String label, String value){ ExtendedCustomCheckbox checkBox = new ExtendedCustomCheckbox(parent, SWT.CHECK, value + ".id"); // $NON-NLS-1$ checkBox.setText(label); checkBox.setToolTipText(label); checkBox.setCheckedValue(value); SWTUtils.setBackgroundColor(checkBox); return checkBox; } /** * If there was already a value set then we need to see which checkBoxes should be checked based on that value. */ private void updateCheckboxState(){ //for each of the checkBoxes, get their checkedValue which is the int bit flag for that checkBox. //If the currently set value contains that flag, then we set the checkBox to be checked if(StringUtil.isNotEmpty(_value)){ try{ int valueInteger = Integer.decode(_value); if(valueInteger>0){ for(int i=0; i<_optionCheckBoxes.size(); i++){ ExtendedCustomCheckbox checkBox = _optionCheckBoxes.get(i); String checkBoxValue = checkBox.getCheckedValue(); if(StringUtil.isNotEmpty(checkBoxValue)){ int checkBoxValueInteger = Integer.decode(checkBoxValue); if(checkBoxValueInteger>0){ if((valueInteger & checkBoxValueInteger)==checkBoxValueInteger){ checkBox.setSelection(true); } } } } } } catch(NumberFormatException nfe){ //failed to parse hex value. } } } /* * (non-Javadoc) * @see com.ibm.commons.swt.data.dialog.LWPDCommonDialog#performDialogOperation(org.eclipse.core.runtime.IProgressMonitor) */ @Override protected boolean performDialogOperation(IProgressMonitor progressMonitor) { int combinedOptions = 0; for(int i=0; i<_optionCheckBoxes.size(); i++){ ExtendedCustomCheckbox checkBox = _optionCheckBoxes.get(i); //if the checkBox is checked, get it's int bit flag value and OR it to the current combined int value. if(checkBox.getSelection()){ String value = checkBox.getCheckedValue(); if(StringUtil.isNotEmpty(value)){ int valueInteger = Integer.decode(value); if(valueInteger >= 0){ combinedOptions = combinedOptions | valueInteger; } } } } if(combinedOptions>0){ _value = ""+combinedOptions; } else{ _value = ""; } return true; } public String getValue(){ return _value; } /* * (non-Javadoc) * @see com.ibm.commons.swt.data.dialog.LWPDCommonDialog#getDialogTitle() */ protected String getDialogTitle(){ return DIALOG_TITLE; } /* * (non-Javadoc) * @see org.eclipse.jface.dialogs.TitleAreaDialog#getInitialSize() */ @Override protected Point getInitialSize() { int w = convertHorizontalDLUsToPixels(INITIAL_DIALOG_WIDTH); int h = convertVerticalDLUsToPixels(INITIAL_DIALOG_HEIGHT); return new Point(w, h); } @Override protected boolean needsProgressMonitor() { return false; } } /** * This class is just an extension of the CustomCheckBox that is used to store a checked value, which * is the bit flag assigned to that checkBox. */ private class ExtendedCustomCheckbox extends CustomCheckBox{ String _checkedValue = "true"; // $NON-NLS-1$ public ExtendedCustomCheckbox(Composite parent, int style, String id) { super(parent, style, id); } public String getCheckedValue() { return _checkedValue; } public void setCheckedValue(String checkedValue) { _checkedValue = checkedValue; } } }