/*******************************************************************************
* Copyright (c) 2005-2014, G. Weirich and Elexis
* 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:
* G. Weirich - initial implementation
* M. Descher - added executable link type
* H. Marlovits - added CHECKBOX/CHECKBOXTRISTATE
*******************************************************************************/
package ch.elexis.core.ui.util;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.util.Map;
import org.eclipse.jface.viewers.ComboViewer;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.StructuredViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
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.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.List;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.forms.widgets.ColumnLayout;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.tiff.common.ui.datepicker.DatePickerCombo;
import ch.elexis.core.constants.StringConstants;
import ch.elexis.core.exceptions.PersistenceException;
import ch.elexis.core.interfaces.INumericEnum;
import ch.elexis.core.ui.UiDesk;
import ch.elexis.core.ui.locks.IUnlockable;
import ch.elexis.data.PersistentObject;
import ch.rgw.tools.ExHandler;
import ch.rgw.tools.Money;
import ch.rgw.tools.StringTool;
import ch.rgw.tools.TimeTool;
/**
* Ein Ein/Ausgabeelement, das aus einem Kästchen mit darin einem Label und darunter einem Control
* zur Eingabe besteht. Eignet sich besonders zur Verwendung in einem ColumnLayout.
*
* @author Gerry
*
*/
public class LabeledInputField extends Composite {
final static Logger logger = LoggerFactory.getLogger(LabeledInputField.class);
static public enum Typ {
TEXT, CHECKBOX, CHECKBOXTRISTATE, LIST, LINK, DATE, MONEY, COMBO, EXECLINK, COMBO_VIEWER
};
Label lbl;
Control ctl;
StructuredViewer viewer;
FormToolkit tk = UiDesk.getToolkit();
Typ inputFieldType;
/**
* simply creates a LabeledInputField of Type LabeledInputField.Typ.TEXT}
*
* @param parent
* @param label
* the label to show above the field
*/
public LabeledInputField(Composite parent, String label){
this(parent, label, Typ.TEXT, Text.LIMIT);
}
public LabeledInputField(Composite parent, String label, Typ typ){
this(parent, label, typ, Text.LIMIT);
}
/**
* Create a {@link LabeledInputField} of {@link Typ#TEXT} with a limited
* amount of input characters
* @param parent
* @param label
* @param limit the limit, or Text.LIMIT if not applicable
*/
public LabeledInputField(Composite parent, String label, int limit){
this(parent, label, Typ.TEXT, limit);
}
/**
* creates a LabeledInputField of the desired Type.
*
* @param parent
* @param label
* the label to show above the field
* @param typ
* the type of field to create. One of LabeledInputField.Typ
*/
public LabeledInputField(Composite parent, String label, Typ typ, int limit){
super(parent, SWT.NONE);
setLayout(new GridLayout(1, false));
this.inputFieldType = typ;
lbl = new Label(this, SWT.BOLD);
switch (typ) {
case CHECKBOX:
case CHECKBOXTRISTATE:
// just a a spacer for nice alignment - label is shown behind the checkbox as usual
break;
default:
lbl.setText(label);
break;
}
switch (typ) {
case LINK:
lbl.setForeground(UiDesk.getColorRegistry().get(UiDesk.COL_BLUE)); //$NON-NLS-1$
ctl = tk.createText(this, "", SWT.NONE);
ctl.setLayoutData(SWTHelper.getFillGridData(1, true, 1, false));
break;
case TEXT:
case MONEY:
ctl = tk.createText(this, "", SWT.BORDER);
((Text) ctl).setTextLimit(limit);
ctl.setLayoutData(SWTHelper.getFillGridData(1, true, 1, false));
break;
case LIST:
ctl = new List(this, SWT.MULTI | SWT.BORDER);
ctl.setLayoutData(new GridData(GridData.FILL_BOTH/* |GridData.GRAB_VERTICAL */));
break;
case DATE:
ctl = new DatePickerCombo(this, SWT.NONE);
ctl.setLayoutData(SWTHelper.getFillGridData(1, true, 1, false));
setText("");
break;
case COMBO:
ctl = new Combo(this, SWT.SINGLE | SWT.BORDER);
ctl.setLayoutData(new GridData(GridData.FILL_BOTH/* |GridData.GRAB_VERTICAL */));
break;
case COMBO_VIEWER:
viewer = new ComboViewer(this, SWT.SINGLE | SWT.BORDER);
ctl = viewer.getControl();
ctl.setLayoutData(new GridData(GridData.FILL_BOTH/* |GridData.GRAB_VERTICAL */));
break;
case CHECKBOX:
ctl = tk.createButton(this, label, SWT.CHECK);
((Button) ctl).setText(label);
ctl.setBackground(this.getBackground());
ctl.setLayoutData(SWTHelper.getFillGridData(1, true, 1, false));
break;
case CHECKBOXTRISTATE:
ctl = new TristateCheckbox(this, SWT.NONE, true);
((Button) ctl).setText(label);
ctl.setBackground(this.getBackground());
ctl.setLayoutData(SWTHelper.getFillGridData(1, true, 1, false));
break;
case EXECLINK:
lbl.setForeground(UiDesk.getColorRegistry().get(UiDesk.COL_BLUE));
ctl = tk.createText(this, StringConstants.EMPTY, SWT.BORDER);
ctl.setLayoutData(SWTHelper.getFillGridData(1, true, 1, false));
break;
default:
break;
}
lbl.setLayoutData(SWTHelper.getFillGridData(1, true, 1, false));
}
/**
* Sets the item's text for TEXT, LIST, LINK, DATE, MONEY, COMBO, EXECLINK. For COMBO and LIST
* it sets the new text only if present as a possible selection. For BOOL, BOOLTRISTATE it sets
* the label of the control.
*
* @param text
* the text to be set
*
*/
public void setText(String text){
if(viewer != null) {
// handled by viewer
return;
}
if (ctl instanceof Text) {
((Text) ctl).setText(text);
} else if (ctl instanceof List) {
List list = (List) ctl;
list.deselectAll();
if (!StringTool.isNothing(text)) {
String[] sel = text.split(",");
int[] selidx = new int[sel.length];
String[] items = list.getItems();
for (int i = 0; i < sel.length; i++) {
int idx = StringTool.getIndex(items, sel[i]);
if (idx != -1) {
selidx[i] = idx;
}
}
list.select(selidx);
}
} else if (ctl instanceof Combo) {
Combo combo = (Combo) ctl;
if (!StringTool.isNothing(text)) {
int idx = StringTool.getIndex(combo.getItems(), text);
if (idx != -1) {
combo.select(idx);
}
}
} else if (ctl instanceof DatePickerCombo) {
DatePickerCombo dp = (DatePickerCombo) ctl;
dp.setDate(new TimeTool(text).getTime());
} else if (ctl instanceof Button) {
((Button) ctl).setText(text);
}
}
/**
* get the String/the selected item/text for TEXT, MONEY, LINK, EXECLINK, LIST, COMBO, DATE. For
* BOOL, BOOLTRISTATE it returns the label of the control.
*
* @return
*/
public String getText(){
if (viewer != null) {
StructuredSelection ss = (StructuredSelection) viewer.getSelection();
Object firstElement = ss.getFirstElement();
if (firstElement == null) {
return StringConstants.EMPTY;
}
if (firstElement instanceof INumericEnum) {
return Integer.toString(((INumericEnum) firstElement).numericValue());
}
return ss.getFirstElement().toString();
}
if (ctl instanceof Text) {
// for TEXT, MONEY, LINK, EXECLINK
return ((Text) ctl).getText();
} else if (ctl instanceof List) {
List list = (List) ctl;
String[] sel = list.getSelection();
if (sel.length == 0) {
return "";
} else {
return StringTool.join(sel, StringConstants.COMMA);
}
} else if (ctl instanceof Combo) {
return ((Combo) ctl).getText();
} else if (ctl instanceof DatePickerCombo) {
return ((DatePickerCombo) ctl).getText();
} else if (ctl instanceof Button) {
return ((Button) ctl).getText();
}
return "";
}
/**
* sets the label for the LabeledInputField
*
* @param text
* the new label
*/
public void setLabel(String text){
switch (inputFieldType) {
case CHECKBOX:
((Button) ctl).setText(text);
case CHECKBOXTRISTATE:
((TristateCheckbox) ctl).setText(text);
break;
default:
lbl.setText(text);
break;
}
}
/**
* gets the label for the LabeledInputField
*
* @return the label for this LabeledInputField
*/
public String getLabel(){
switch (inputFieldType) {
case CHECKBOX:
return ((Button) ctl).getText();
case CHECKBOXTRISTATE:
return ((TristateCheckbox) ctl).getText();
default:
return lbl.getText();
}
}
public Label getLabelComponent(){
return lbl;
}
public Control getControl(){
return ctl;
}
public StructuredViewer getViewer() {
return viewer;
}
public static class Tableau extends Composite {
public Tableau(Composite parent, int minColumns, int maxColumns){
super(parent, SWT.BORDER);
ColumnLayout cl = new ColumnLayout();
cl.maxNumColumns = maxColumns;
cl.minNumColumns = minColumns;
setLayout(cl);
}
public Tableau(Composite parent){
this(parent, 1, 5);
}
public LabeledInputField addComponent(String l){
return new LabeledInputField(this, l);
}
public LabeledInputField addComponent(String l, LabeledInputField.Typ typ){
return new LabeledInputField(this, l, typ);
}
public LabeledInputField addComponent(String l, int limit){
return new LabeledInputField(this, l, limit);
}
}
/**
* class for storing display details for use with LabeledInputField
*/
public static class InputData {
public enum Typ {
STRING, INT, CURRENCY, LIST, HYPERLINK, DATE, COMBO, EXECSTRING, CHECKBOX,
CHECKBOXTRISTATE, COMBO_VIEWER
};
String sAnzeige, sFeldname, sHashname;
Typ tFeldTyp;
Object ext;
LabeledInputField mine;
int sLimit;
private org.eclipse.jface.viewers.IContentProvider contentProvider;
private ILabelProvider labelProvider;
private IStructuredSelectionResolver selectionResolver;
/**
* create control of different types.
*
* @param anzeige
* String, the label shown above the field
* @param feldname
* the database field name
* @param feldtyp
* field type to use, one of InputData.Typ
* @param hashname
* the name of the field in the hashfield, set to null if feldname is not a hash
*/
public InputData(String anzeige, String feldname, Typ feldtyp, String hashname){
this(anzeige, feldname, feldtyp, hashname, Text.LIMIT);
}
/**
* create control of different types.
*
* @param anzeige
* String, the label shown above the field
* @param feldname
* the database field name
* @param feldtyp
* field type to use, one of InputData.Typ
* @param hashname
* the name of the field in the hashfield, set to null if feldname is not a hash
* @param limit
* the max. allow characters for this field
*/
public InputData(String anzeige, String feldname, Typ feldtyp, String hashname, int limit){
sAnzeige = anzeige;
sFeldname = feldname;
tFeldTyp = feldtyp;
sHashname = hashname;
sLimit = limit;
}
/**
* create control of type STRING. label and fieldType are the same
*
* @param all
* the fieldname, also used for the label
*/
public InputData(String all){
this(all, all, Typ.STRING, null, Text.LIMIT);
}
/**
* create control of type HYPERLINK
*
* @param anzeige
* String, the label shown above the field
* @param feldname
* the database field name
* @param cp
* an IContentProvider used to display the contents
*/
public InputData(String anzeige, String feldname, IContentProvider cp){
sAnzeige = anzeige;
sFeldname = feldname;
ext = cp;
tFeldTyp = Typ.HYPERLINK;
sLimit = Text.LIMIT;
}
public InputData(String anzeige, String feldname, String hashname, Typ typ,
org.eclipse.jface.viewers.IContentProvider contentProvider,
ILabelProvider labelProvider, IStructuredSelectionResolver selectionResolver, Object input){
sAnzeige = anzeige;
sFeldname = feldname;
sHashname = hashname;
ext = input;
tFeldTyp = typ;
this.contentProvider = contentProvider;
this.labelProvider = labelProvider;
this.selectionResolver = selectionResolver;
}
/**
* Provide an executable link input field; that is, on click on the resp. label (like the
* hyperlink), an executable can be called by a callback on cp
*
* @param anzeige
* the name of the label (will be clickable, calling cp)
* @param feldname
* the field name
* @param feldtyp
* the field type
* @param cp
* executable callback
* @author M. Descher
*/
public InputData(String anzeige, String feldname, IExecLinkProvider cp){
this(anzeige, feldname, Typ.EXECSTRING, null, Text.LIMIT);
ext = cp;
}
/**
* create control of type LIST
*
* @param anzeige
* String, the label shown above the field
* @param feldname
* the database field name
* @param hashname
* the name of the field in the hashfield, set to null if feldname is not a hash
* @param choices
* the items to be displayed in the list
*/
public InputData(String anzeige, String feldname, String hashname, String[] choices){
this(anzeige, feldname, Typ.LIST, hashname, Text.LIMIT);
ext = choices;
}
/**
* create control of type COMBO
*
* @param anzeige
* String, the label shown above the field
* @param feldname
* the database field name
* @param hashname
* the name of the field in the hashfield, set to null if feldname is not a hash
* @param comboItems
* the items to be displayed in the combo
* @param bDropDown
* just to select this method - not used actually...
*/
public InputData(String anzeige, String feldname, String hashname, String[] comboItems,
boolean bDropDown){
this(anzeige, feldname, Typ.COMBO, hashname, Text.LIMIT);
ext = comboItems;
}
public void setParent(LabeledInputField p){
mine = p;
}
public void setLabel(String lbl){
if (mine != null) {
mine.setLabel(lbl);
}
}
public String getLabel(){
return mine == null ? "" : mine.getLabel();
}
public String getText(){
return mine == null ? "" : mine.getText();
}
public void setText(String t){
if (mine != null) {
mine.setText(t);
}
}
public LabeledInputField getWidget(){
return mine;
}
public void setEditable(boolean ed){
mine.lbl.setEnabled(ed);
if(tFeldTyp==Typ.EXECSTRING || tFeldTyp==Typ.HYPERLINK) {
mine.lbl.setEnabled(true);
((Text) mine.ctl).setEnabled(ed);
return;
}
if(mine.ctl instanceof Text) {
((Text) mine.ctl).setEditable(ed);
} else {
mine.ctl.setEnabled(ed);
}
}
public void setChoices(String... strings){
ext = strings;
}
}
/**
* Create an automatically maintained form out of an array of InpuData[].<br>
* Usage: <code><nowrap><br><ul>
* InputData[] id=new InputData[]{ // ... };<br>
* TableWrapLayout twl=new TableWrapLayout();<br>
* setLayout(twl);<br>
* AutoForm af=new LabeledInputField.AutoForm(parent,id));<br>
* TableWrapData twd=new TableWrapData(TableWrapData.FILL_GRAB);<br>
* twd.grabHorizontal=true;<br>af.setLayoutData(twd);</ul><br></code>
*/
public static class AutoForm extends Tableau implements IUnlockable {
InputData[] def;
Control[] cFields;
PersistentObject act;
DecimalFormat df = new DecimalFormat(Messages.LabeledInputField_7); //$NON-NLS-1$
LabeledInputField ltf;
public AutoForm(Composite parent, InputData[] fields){
this(parent, fields, 1, 5);
}
public AutoForm(Composite parent, InputData[] fields, int minColumns, int maxColumns){
super(parent, minColumns, maxColumns);
def = fields;
cFields = new Control[def.length];
for (int i = 0; i < def.length; i++) {
ltf = null;
InputData.Typ typ = def[i].tFeldTyp;
if (typ == InputData.Typ.LIST) {
ltf = addComponent(def[i].sAnzeige, LabeledInputField.Typ.LIST);
((List) ltf.getControl()).setItems((String[]) def[i].ext);
} else if (typ == InputData.Typ.COMBO) {
ltf = addComponent(def[i].sAnzeige, LabeledInputField.Typ.COMBO);
((Combo) ltf.getControl()).setItems((String[]) def[i].ext);
} else if (typ == InputData.Typ.CHECKBOX) {
ltf = addComponent(def[i].sAnzeige, LabeledInputField.Typ.CHECKBOX);
} else if (typ == InputData.Typ.CHECKBOXTRISTATE) {
ltf = addComponent(def[i].sAnzeige, LabeledInputField.Typ.CHECKBOXTRISTATE);
} else if (typ == InputData.Typ.EXECSTRING) {
ltf = addComponent(def[i].sAnzeige, LabeledInputField.Typ.EXECLINK);
ltf.lbl.setData(i);
ltf.lbl.addMouseListener(new MouseAdapter() {
@Override
public void mouseDown(MouseEvent e){
Label l = (Label) e.getSource();
int i = (Integer) l.getData();
((IExecLinkProvider) def[i].ext).executeString(def[i]);
super.mouseDown(e);
}
});
} else if (typ == InputData.Typ.COMBO_VIEWER){
ltf = addComponent(def[i].sAnzeige, LabeledInputField.Typ.COMBO_VIEWER);
ltf.getViewer().setContentProvider(def[i].contentProvider);
ltf.getViewer().setLabelProvider(def[i].labelProvider);
ltf.getViewer().setInput(def[i].ext);
} else {
if (typ == InputData.Typ.HYPERLINK) {
ltf = addComponent(def[i].sAnzeige, LabeledInputField.Typ.LINK);
ltf.lbl.setData(i);
ltf.lbl.addMouseListener(new MouseAdapter() {
@Override
public void mouseDown(MouseEvent e){
Label l = (Label) e.getSource();
int i = (Integer) l.getData();
((IContentProvider) def[i].ext).reloadContent(act, def[i]);
super.mouseDown(e);
}
});
((Text) ltf.ctl).setEditable(false);
} else {
ltf = addComponent(def[i].sAnzeige, def[i].sLimit);
}
}
def[i].setParent(ltf);
cFields[i] = ltf.getControl();
cFields[i].addFocusListener(new FocusListener() {
public void focusGained(FocusEvent e){}
@SuppressWarnings("unchecked")
public void focusLost(FocusEvent e){
if (act != null) {
Control src = (Control) e.getSource();
InputData inp = (InputData) src.getData();
save(inp);
}
}
});
cFields[i].setData(def[i]);
}
}
public void save(){
for (InputData id : def) {
save(id);
}
}
protected void save(InputData inp){
if (act == null) {
return;
}
String val = StringTool.leer;
switch (inp.tFeldTyp) {
case STRING:
case COMBO:
case COMBO_VIEWER:
case INT:
case EXECSTRING:
case DATE:
val = inp.getText();
break;
case CURRENCY:
try {
Money money = new Money(inp.getText());
val = money.getCentsAsString();
} catch (ParseException e1) {
ExHandler.handle(e1);
val = "";
}
// double betr=Double.parseDouble(inp.getText())*100.0;
// val=Long.toString(Math.round(betr));
break;
case LIST:
val = inp.getText();
break;
case CHECKBOX:
val = (((Button) (inp.mine.getControl())).getSelection() == true)
? StringConstants.ONE : StringConstants.ZERO;
break;
case CHECKBOXTRISTATE:
val = ((TristateCheckbox) (inp.mine.getControl())).getTristateDbValue();
break;
case HYPERLINK:
// dont try to save a hyperlink ...
return;
default:
break;
}
if (inp.sHashname == null) {
try {
act.set(inp.sFeldname, val);
} catch (PersistenceException pe) {
logger.error("Could not persist [" + val + "] for field [" + inp.sAnzeige
+ "]\nCause: " + pe.getCause().getMessage(), pe);
if (inp.tFeldTyp
.equals(ch.elexis.core.ui.util.LabeledInputField.InputData.Typ.STRING)) {
// clear cache to always get the actual the DB value
PersistentObject.clearCache();
inp.mine.setText(act.get(inp.sFeldname));
}
}
} else {
if (val != null && val.length() > 0) {
Map ext = act.getMap(inp.sFeldname);
ext.put(inp.sHashname, val);
act.setMap(inp.sFeldname, ext);
}
}
}
/**
* Angezeigte Daten aus DB neu laden
*
* @param o
* Das Objekt aus dem Daten geladen werden
*/
public void reload(PersistentObject o){
act = o;
if (o == null) {
for (int i = 0; i < def.length; i++) {
def[i].setText(StringTool.leer);
}
return;
}
act = o;
String val = StringTool.leer;
for (int i = 0; i < def.length; i++) {
if (def[i].tFeldTyp == InputData.Typ.HYPERLINK) {
((IContentProvider) def[i].ext).displayContent(o, def[i]);
continue;
} else {
if (def[i].sHashname == null) {
val = o.get(def[i].sFeldname);
} else {
Map ext = o.getMap(def[i].sFeldname);
val = (String) ext.get(def[i].sHashname);
// needed to make artikelstamm dialog work properly (without **ERROR:...)
if (val == null) {
val = o.get(def[i].sHashname);
// in case no value exists for this field keep it empty
if (val.startsWith(PersistentObject.MAPPING_ERROR_MARKER)) {
val = null;
}
}
}
}
switch (def[i].tFeldTyp) {
case STRING:
case INT:
case LIST:
case COMBO:
case EXECSTRING:
case DATE:
if (StringTool.isNothing(val)) {
val = StringTool.leer;
}
def[i].setText(val);
break;
case CURRENCY:
Money money = new Money(PersistentObject.checkZero(val));
def[i].setText(money.getAmountAsString());
// double betr=PersistentObject.checkZeroDouble(val);
// def[i].setText(Double.toString(betr/100.0));
break;
case CHECKBOX:
val = StringTool.unNull(val);
((Button) (def[i].mine.getControl()))
.setSelection(val.equalsIgnoreCase(StringConstants.ONE));
break;
case CHECKBOXTRISTATE:
val = StringTool.unNull(val);
((TristateCheckbox) (def[i].mine.getControl())).setTristateDbValue(val);
break;
case COMBO_VIEWER:
StructuredSelection selection =
def[i].selectionResolver.resolveStructuredSelection(val);
((StructuredViewer) (def[i].mine.getViewer())).setSelection(selection);
break;
}
}
}
@Override
public void setUnlocked(boolean unlocked){
for (InputData id : def) {
id.setEditable(unlocked);
}
}
}
public interface IContentProvider {
/** fetch the Content from the defining PersistentObject and display it in ltf */
public void displayContent(PersistentObject po, InputData ltf);
/** Let the user modify the content and load in in po and ltf */
public void reloadContent(PersistentObject po, InputData ltf);
}
public interface IExecLinkProvider {
/** Execute the string within the InputData */
public void executeString(InputData ltf);
}
public interface IStructuredSelectionResolver {
public StructuredSelection resolveStructuredSelection(String value);
}
}