/*******************************************************************************
* Copyright (c) 2000, 2005, 2006 IBM Corporation, JBoss Inc. and others.
* 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:
* IBM Corporation - initial API and implementation
* Max Rydahl Andersen, JBoss Inc. - added tooltip description support
*******************************************************************************/
package org.hibernate.eclipse.mapper.editors.reveng.xpl;
import java.text.BreakIterator;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.FocusAdapter;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Layout;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.forms.IFormColors;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.Hyperlink;
import org.eclipse.ui.forms.widgets.TableWrapData;
import org.eclipse.ui.forms.widgets.TableWrapLayout;
import org.hibernate.eclipse.mapper.editors.reveng.IFormTextEntryListener;
/**
* Helper to have a label/text entry field in a Form.
*
* Supports hyperlinking of the label and optionally a button after the text.
*
* From the eclipse forms internal package. The attached listener reacts to all
* the events. Entring new text makes the entry 'dirty', but only when 'commit'
* is called is 'valueChanged' method called (and only if 'dirty' flag is set).
* This allows delayed commit.
*/
public class FormTextEntry {
private static final int TOOLTIP_WIDTH_LIMIT = 300;
private Control label;
private Text text;
private Button browse;
private String value=""; //$NON-NLS-1$
private String description;
private boolean dirty;
boolean ignoreModify = false;
private IFormTextEntryListener listener;
/**
* The default constructor. Call 'createControl' to make it.
*
*/
public FormTextEntry(Composite parent, FormToolkit toolkit, String labelText, int style) {
createControl(parent, toolkit, labelText, style, null, false, 0);
}
/**
* This constructor create all the controls right away.
*
* @param parent
* @param toolkit
* @param labelText
* @param browseText
* @param linkLabel
*/
public FormTextEntry(Composite parent, FormToolkit toolkit, String labelText,
String browseText, boolean linkLabel) {
this(parent, toolkit, labelText, browseText, linkLabel, 0);
}
public FormTextEntry(Composite parent, FormToolkit toolkit, String labelText,
String browseText, boolean linkLabel, int indent) {
createControl(parent, toolkit, labelText, SWT.SINGLE, browseText, linkLabel, indent);
}
/** all constructor */
public FormTextEntry(Composite parent, FormToolkit toolkit, String labelText,
int style, String browseText, boolean linkLabel, int indent) {
createControl(parent, toolkit, labelText, style, browseText, linkLabel, indent);
}
/**
* Create all the controls in the provided parent.
*
* @param parent
* @param toolkit
* @param labelText
* @param span
* @param browseText
* @param linkLabel
*/
private void createControl(Composite parent, FormToolkit toolkit,
String labelText, int style, String browseText, boolean linkLabel, int indent) {
if (linkLabel) {
Hyperlink link = toolkit.createHyperlink(parent, labelText,
SWT.NULL);
label = link;
} else {
label = toolkit.createLabel(parent, labelText);
label.setForeground(toolkit.getColors().getColor(IFormColors.TITLE));
label.setToolTipText(getToolTipText(label));
}
text = toolkit.createText(parent, "", style); //$NON-NLS-1$
addListeners();
if (browseText != null) {
browse = toolkit.createButton(parent, browseText, SWT.PUSH);
browse.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
if (listener != null)
listener.browseButtonSelected(FormTextEntry.this);
}
});
}
fillIntoGrid(parent, indent);
}
public void setEditable(boolean editable) {
text.setEditable(editable);
if (browse!=null)
browse.setEnabled(editable);
}
public void setEnabled(boolean enabled) {
}
private void fillIntoGrid(Composite parent, int indent) {
Layout layout = parent.getLayout();
if (layout instanceof GridLayout) {
GridData gd;
int span = ((GridLayout) layout).numColumns;
gd = new GridData(GridData.VERTICAL_ALIGN_CENTER);
gd.horizontalIndent = indent;
label.setLayoutData(gd);
int tspan = browse != null ? span - 2 : span - 1;
gd = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
gd.horizontalSpan = tspan;
gd.grabExcessHorizontalSpace = (tspan == 1);
gd.widthHint = 10;
text.setLayoutData(gd);
if (browse != null) {
gd = new GridData(GridData.VERTICAL_ALIGN_CENTER);
browse.setLayoutData(gd);
}
} else if (layout instanceof TableWrapLayout) {
TableWrapData td;
int span = ((TableWrapLayout) layout).numColumns;
td = new TableWrapData();
td.valign = TableWrapData.MIDDLE;
td.indent = indent;
label.setLayoutData(td);
int tspan = browse != null ? span - 2 : span - 1;
td = new TableWrapData(TableWrapData.FILL);
td.colspan = tspan;
td.grabHorizontal = (tspan == 1);
td.valign = TableWrapData.MIDDLE;
text.setLayoutData(td);
if (browse != null) {
td = new TableWrapData();
td.valign = TableWrapData.MIDDLE;
browse.setLayoutData(td);
}
}
}
/**
* Attaches the listener for the entry.
*
* @param listener
*/
public void setFormEntryListener(IFormTextEntryListener listener) {
if (label instanceof Hyperlink) {
if (this.listener!=null)
((Hyperlink)label).removeHyperlinkListener(this.listener);
if (listener!=null)
((Hyperlink)label).addHyperlinkListener(listener);
}
this.listener = listener;
}
private void addListeners() {
text.addKeyListener(new KeyAdapter() {
public void keyReleased(KeyEvent e) {
keyReleaseOccured(e);
}
});
text.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
editOccured(e);
}
});
text.addFocusListener(new FocusAdapter() {
public void focusGained(FocusEvent e) {
if (listener != null)
listener.focusGained(FormTextEntry.this);
}
public void focusLost(FocusEvent e) {
if (dirty)
commit();
}
});
}
/**
* If dirty, commits the text in the widget to the value and notifies the
* listener. This call clears the 'dirty' flag.
*
*/
public void commit() {
if (dirty) {
value = text.getText();
//if (value.length()==0)
//value = null;
//notify
if (listener != null)
listener.textValueChanged(this);
}
dirty = false;
}
public void cancelEdit() {
dirty = false;
}
private void editOccured(ModifyEvent e) {
if (ignoreModify)
return;
dirty = true;
if (listener != null)
listener.textDirty(this);
}
/**
* Returns the text control.
*
* @return
*/
public Text getText() {
return text;
}
/**
* Returns the browse button control.
* @return
*/
public Button getButton() {
return browse;
}
/**
* Returns the current entry value. If the entry is dirty and was not
* commited, the value may be different from the text in the widget.
*
* @return
*/
public String getValue() {
return value.trim();
}
/**
* Returns true if the text has been modified.
*
* @return
*/
public boolean isDirty() {
return dirty;
}
private void keyReleaseOccured(KeyEvent e) {
if (e.character == '\r') {
// commit value
if (dirty)
commit();
} else if (e.character == '\u001b') { // Escape character
text.setText(value != null ? value : ""); // restore old //$NON-NLS-1$
dirty = false;
}
listener.selectionChanged(FormTextEntry.this);
}
/**
* Sets the value of this entry.
*
* @param value
*/
public void setValue(String value) {
if (text != null)
text.setText(value != null ? value : ""); //$NON-NLS-1$
this.value = (value != null) ? value : ""; //$NON-NLS-1$
}
/**
* Sets the value of this entry with the possibility to turn the
* notification off.
*
* @param value
* @param blockNotification
*/
public void setValue(String value, boolean blockNotification) {
ignoreModify = blockNotification;
setValue(value);
ignoreModify = false;
}
protected String getToolTipText(Control control) {
String text = getDescription();
if (text==null) return null;
int dot = text.indexOf('.');
if (dot != -1) {
StringBuffer buf = new StringBuffer();
boolean inTag=false;
for (int i=0; i<text.length(); i++) {
char c = text.charAt(i);
if (inTag) {
if (c=='>') {
inTag = false;
continue;
}
}
else {
if (c=='<') {
inTag = true;
continue;
}
else if (c=='.') {
if (i<text.length()-1) {
char c2 = text.charAt(i+1);
if (c2==' ' || c2=='\t' || c2=='\n') break;
}
}
buf.append(c);
}
}
return wrapText(control, buf.toString(), TOOLTIP_WIDTH_LIMIT);
}
return text;
}
private String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
if(label!=null) {
label.setToolTipText(getToolTipText(label));
}
}
private String wrapText(Control c, String src, int width) {
BreakIterator wb = BreakIterator.getWordInstance();
wb.setText(src);
int saved = 0;
int last = 0;
StringBuffer buff = new StringBuffer();
GC gc = new GC(c);
for (int loc = wb.first(); loc != BreakIterator.DONE; loc = wb.next()) {
String word = src.substring(saved, loc);
Point extent = gc.textExtent(word);
if (extent.x > width) {
// overflow
String prevLine = src.substring(saved, last);
buff.append(prevLine);
buff.append(SWT.LF);
saved = last;
}
last = loc;
}
gc.dispose();
String lastLine = src.substring(saved, last);
buff.append(lastLine);
return buff.toString();
}
}