001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jface.viewers;
011:
012: import java.text.MessageFormat; // Not using ICU to support standalone JFace scenario
013:
014: import org.eclipse.jface.resource.ImageDescriptor;
015: import org.eclipse.jface.resource.ImageRegistry;
016: import org.eclipse.jface.resource.JFaceResources;
017: import org.eclipse.swt.SWT;
018: import org.eclipse.swt.events.FocusEvent;
019: import org.eclipse.swt.events.FocusListener;
020: import org.eclipse.swt.events.KeyAdapter;
021: import org.eclipse.swt.events.KeyEvent;
022: import org.eclipse.swt.events.SelectionAdapter;
023: import org.eclipse.swt.events.SelectionEvent;
024: import org.eclipse.swt.graphics.Color;
025: import org.eclipse.swt.graphics.Font;
026: import org.eclipse.swt.graphics.Point;
027: import org.eclipse.swt.graphics.Rectangle;
028: import org.eclipse.swt.widgets.Button;
029: import org.eclipse.swt.widgets.Composite;
030: import org.eclipse.swt.widgets.Control;
031: import org.eclipse.swt.widgets.Label;
032: import org.eclipse.swt.widgets.Layout;
033:
034: /**
035: * An abstract cell editor that uses a dialog.
036: * Dialog cell editors usually have a label control on the left and a button on
037: * the right. Pressing the button opens a dialog window (for example, a color dialog
038: * or a file dialog) to change the cell editor's value.
039: * The cell editor's value is the value of the dialog.
040: * <p>
041: * Subclasses may override the following methods:
042: * <ul>
043: * <li><code>createButton</code>: creates the cell editor's button control</li>
044: * <li><code>createContents</code>: creates the cell editor's 'display value' control</li>
045: * <li><code>updateContents</code>: updates the cell editor's 'display value' control
046: * after its value has changed</li>
047: * <li><code>openDialogBox</code>: opens the dialog box when the end user presses
048: * the button</li>
049: * </ul>
050: * </p>
051: */
052: public abstract class DialogCellEditor extends CellEditor {
053:
054: /**
055: * Image registry key for three dot image (value <code>"cell_editor_dots_button_image"</code>).
056: */
057: public static final String CELL_EDITOR_IMG_DOTS_BUTTON = "cell_editor_dots_button_image";//$NON-NLS-1$
058:
059: /**
060: * The editor control.
061: */
062: private Composite editor;
063:
064: /**
065: * The current contents.
066: */
067: private Control contents;
068:
069: /**
070: * The label that gets reused by <code>updateLabel</code>.
071: */
072: private Label defaultLabel;
073:
074: /**
075: * The button.
076: */
077: private Button button;
078:
079: /**
080: * Listens for 'focusLost' events and fires the 'apply' event as long
081: * as the focus wasn't lost because the dialog was opened.
082: */
083: private FocusListener buttonFocusListener;
084:
085: /**
086: * The value of this cell editor; initially <code>null</code>.
087: */
088: private Object value = null;
089:
090: static {
091: ImageRegistry reg = JFaceResources.getImageRegistry();
092: reg.put(CELL_EDITOR_IMG_DOTS_BUTTON, ImageDescriptor
093: .createFromFile(DialogCellEditor.class,
094: "images/dots_button.gif"));//$NON-NLS-1$
095: }
096:
097: /**
098: * Internal class for laying out the dialog.
099: */
100: private class DialogCellLayout extends Layout {
101: public void layout(Composite editor, boolean force) {
102: Rectangle bounds = editor.getClientArea();
103: Point size = button.computeSize(SWT.DEFAULT, SWT.DEFAULT,
104: force);
105: if (contents != null) {
106: contents.setBounds(0, 0, bounds.width - size.x,
107: bounds.height);
108: }
109: button.setBounds(bounds.width - size.x, 0, size.x,
110: bounds.height);
111: }
112:
113: public Point computeSize(Composite editor, int wHint,
114: int hHint, boolean force) {
115: if (wHint != SWT.DEFAULT && hHint != SWT.DEFAULT) {
116: return new Point(wHint, hHint);
117: }
118: Point contentsSize = contents.computeSize(SWT.DEFAULT,
119: SWT.DEFAULT, force);
120: Point buttonSize = button.computeSize(SWT.DEFAULT,
121: SWT.DEFAULT, force);
122: // Just return the button width to ensure the button is not clipped
123: // if the label is long.
124: // The label will just use whatever extra width there is
125: Point result = new Point(buttonSize.x, Math.max(
126: contentsSize.y, buttonSize.y));
127: return result;
128: }
129: }
130:
131: /**
132: * Default DialogCellEditor style
133: */
134: private static final int defaultStyle = SWT.NONE;
135:
136: /**
137: * Creates a new dialog cell editor with no control
138: * @since 2.1
139: */
140: public DialogCellEditor() {
141: setStyle(defaultStyle);
142: }
143:
144: /**
145: * Creates a new dialog cell editor parented under the given control.
146: * The cell editor value is <code>null</code> initially, and has no
147: * validator.
148: *
149: * @param parent the parent control
150: */
151: protected DialogCellEditor(Composite parent) {
152: this (parent, defaultStyle);
153: }
154:
155: /**
156: * Creates a new dialog cell editor parented under the given control.
157: * The cell editor value is <code>null</code> initially, and has no
158: * validator.
159: *
160: * @param parent the parent control
161: * @param style the style bits
162: * @since 2.1
163: */
164: protected DialogCellEditor(Composite parent, int style) {
165: super (parent, style);
166: }
167:
168: /**
169: * Creates the button for this cell editor under the given parent control.
170: * <p>
171: * The default implementation of this framework method creates the button
172: * display on the right hand side of the dialog cell editor. Subclasses
173: * may extend or reimplement.
174: * </p>
175: *
176: * @param parent the parent control
177: * @return the new button control
178: */
179: protected Button createButton(Composite parent) {
180: Button result = new Button(parent, SWT.DOWN);
181: result.setText("..."); //$NON-NLS-1$
182: return result;
183: }
184:
185: /**
186: * Creates the controls used to show the value of this cell editor.
187: * <p>
188: * The default implementation of this framework method creates
189: * a label widget, using the same font and background color as the parent control.
190: * </p>
191: * <p>
192: * Subclasses may reimplement. If you reimplement this method, you
193: * should also reimplement <code>updateContents</code>.
194: * </p>
195: *
196: * @param cell the control for this cell editor
197: * @return the underlying control
198: */
199: protected Control createContents(Composite cell) {
200: defaultLabel = new Label(cell, SWT.LEFT);
201: defaultLabel.setFont(cell.getFont());
202: defaultLabel.setBackground(cell.getBackground());
203: return defaultLabel;
204: }
205:
206: /* (non-Javadoc)
207: * Method declared on CellEditor.
208: */
209: protected Control createControl(Composite parent) {
210:
211: Font font = parent.getFont();
212: Color bg = parent.getBackground();
213:
214: editor = new Composite(parent, getStyle());
215: editor.setFont(font);
216: editor.setBackground(bg);
217: editor.setLayout(new DialogCellLayout());
218:
219: contents = createContents(editor);
220: updateContents(value);
221:
222: button = createButton(editor);
223: button.setFont(font);
224:
225: button.addKeyListener(new KeyAdapter() {
226: /* (non-Javadoc)
227: * @see org.eclipse.swt.events.KeyListener#keyReleased(org.eclipse.swt.events.KeyEvent)
228: */
229: public void keyReleased(KeyEvent e) {
230: if (e.character == '\u001b') { // Escape
231: fireCancelEditor();
232: }
233: }
234: });
235:
236: button.addFocusListener(getButtonFocusListener());
237:
238: button.addSelectionListener(new SelectionAdapter() {
239: /* (non-Javadoc)
240: * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent)
241: */
242: public void widgetSelected(SelectionEvent event) {
243: // Remove the button's focus listener since it's guaranteed
244: // to lose focus when the dialog opens
245: button.removeFocusListener(getButtonFocusListener());
246:
247: Object newValue = openDialogBox(editor);
248:
249: // Re-add the listener once the dialog closes
250: button.addFocusListener(getButtonFocusListener());
251:
252: if (newValue != null) {
253: boolean newValidState = isCorrect(newValue);
254: if (newValidState) {
255: markDirty();
256: doSetValue(newValue);
257: } else {
258: // try to insert the current value into the error message.
259: setErrorMessage(MessageFormat.format(
260: getErrorMessage(),
261: new Object[] { newValue.toString() }));
262: }
263: fireApplyEditorValue();
264: }
265: }
266: });
267:
268: setValueValid(true);
269:
270: return editor;
271: }
272:
273: /* (non-Javadoc)
274: *
275: * Override in order to remove the button's focus listener if the celleditor
276: * is deactivating.
277: *
278: * @see org.eclipse.jface.viewers.CellEditor#deactivate()
279: */
280: public void deactivate() {
281: if (button != null && !button.isDisposed()) {
282: button.removeFocusListener(getButtonFocusListener());
283: }
284:
285: super .deactivate();
286: }
287:
288: /* (non-Javadoc)
289: * Method declared on CellEditor.
290: */
291: protected Object doGetValue() {
292: return value;
293: }
294:
295: /* (non-Javadoc)
296: * Method declared on CellEditor.
297: * The focus is set to the cell editor's button.
298: */
299: protected void doSetFocus() {
300: button.setFocus();
301:
302: // add a FocusListener to the button
303: button.addFocusListener(getButtonFocusListener());
304: }
305:
306: /**
307: * Return a listener for button focus.
308: * @return FocusListener
309: */
310: private FocusListener getButtonFocusListener() {
311: if (buttonFocusListener == null) {
312: buttonFocusListener = new FocusListener() {
313:
314: /* (non-Javadoc)
315: * @see org.eclipse.swt.events.FocusListener#focusGained(org.eclipse.swt.events.FocusEvent)
316: */
317: public void focusGained(FocusEvent e) {
318: // Do nothing
319: }
320:
321: /* (non-Javadoc)
322: * @see org.eclipse.swt.events.FocusListener#focusLost(org.eclipse.swt.events.FocusEvent)
323: */
324: public void focusLost(FocusEvent e) {
325: DialogCellEditor.this .focusLost();
326: }
327: };
328: }
329:
330: return buttonFocusListener;
331: }
332:
333: /* (non-Javadoc)
334: * Method declared on CellEditor.
335: */
336: protected void doSetValue(Object value) {
337: this .value = value;
338: updateContents(value);
339: }
340:
341: /**
342: * Returns the default label widget created by <code>createContents</code>.
343: *
344: * @return the default label widget
345: */
346: protected Label getDefaultLabel() {
347: return defaultLabel;
348: }
349:
350: /**
351: * Opens a dialog box under the given parent control and returns the
352: * dialog's value when it closes, or <code>null</code> if the dialog
353: * was canceled or no selection was made in the dialog.
354: * <p>
355: * This framework method must be implemented by concrete subclasses.
356: * It is called when the user has pressed the button and the dialog
357: * box must pop up.
358: * </p>
359: *
360: * @param cellEditorWindow the parent control cell editor's window
361: * so that a subclass can adjust the dialog box accordingly
362: * @return the selected value, or <code>null</code> if the dialog was
363: * canceled or no selection was made in the dialog
364: */
365: protected abstract Object openDialogBox(Control cellEditorWindow);
366:
367: /**
368: * Updates the controls showing the value of this cell editor.
369: * <p>
370: * The default implementation of this framework method just converts
371: * the passed object to a string using <code>toString</code> and
372: * sets this as the text of the label widget.
373: * </p>
374: * <p>
375: * Subclasses may reimplement. If you reimplement this method, you
376: * should also reimplement <code>createContents</code>.
377: * </p>
378: *
379: * @param value the new value of this cell editor
380: */
381: protected void updateContents(Object value) {
382: if (defaultLabel == null) {
383: return;
384: }
385:
386: String text = "";//$NON-NLS-1$
387: if (value != null) {
388: text = value.toString();
389: }
390: defaultLabel.setText(text);
391: }
392: }
|