001: package net.sf.jmoney.model2;
002:
003: import net.sf.jmoney.fields.AccountControl;
004:
005: import org.eclipse.core.runtime.Assert;
006: import org.eclipse.jface.viewers.CellEditor;
007: import org.eclipse.swt.SWT;
008: import org.eclipse.swt.events.FocusAdapter;
009: import org.eclipse.swt.events.FocusEvent;
010: import org.eclipse.swt.events.KeyAdapter;
011: import org.eclipse.swt.events.KeyEvent;
012: import org.eclipse.swt.events.ModifyEvent;
013: import org.eclipse.swt.events.ModifyListener;
014: import org.eclipse.swt.events.MouseAdapter;
015: import org.eclipse.swt.events.MouseEvent;
016: import org.eclipse.swt.events.SelectionAdapter;
017: import org.eclipse.swt.events.SelectionEvent;
018: import org.eclipse.swt.events.TraverseEvent;
019: import org.eclipse.swt.events.TraverseListener;
020: import org.eclipse.swt.graphics.GC;
021: import org.eclipse.swt.widgets.Composite;
022: import org.eclipse.swt.widgets.Control;
023:
024: public class AccountCellEditor<A extends Account> extends CellEditor {
025:
026: Session session;
027: Class<A> classOfAccount;
028:
029: public AccountCellEditor(Composite table, Session session,
030: Class<A> classOfAccount) {
031: super (table, SWT.SINGLE);
032:
033: this .session = session;
034: this .classOfAccount = classOfAccount;
035:
036: /*
037: * The super constructor makes a call to our createControl method. This
038: * method creates the AccountControl. However, the session and classOfAccount fields were not
039: * set at that time, so the AccountControl has a null session and classOfAccount set in it.
040: * We must set the properties before anything happens such as the control
041: * getting focus.
042: */
043: accountControl.setSession(session, classOfAccount);
044: }
045:
046: // Fields if text
047:
048: /**
049: * The text control; initially <code>null</code>.
050: */
051: protected AccountControl<A> accountControl;
052:
053: private ModifyListener modifyListener;
054:
055: /**
056: * State information for updating action enablement
057: */
058: private boolean isAccountSelected = false;
059:
060: /**
061: * Checks to see if the "deleteable" state (can delete/
062: * nothing to delete) has changed and if so fire an
063: * enablement changed notification.
064: */
065: private void checkAccountSelected() {
066: boolean oldIsAccountSelected = isAccountSelected;
067: isAccountSelected = isAccountSelected();
068: if (oldIsAccountSelected != isAccountSelected) {
069: fireEnablementChanged(DELETE);
070: fireEnablementChanged(SELECT_ALL);
071: fireEnablementChanged(COPY);
072: fireEnablementChanged(CUT);
073: }
074: }
075:
076: /* (non-Javadoc)
077: * Method declared on CellEditor.
078: */
079: @Override
080: protected Control createControl(Composite parent) {
081: accountControl = new AccountControl<A>(parent, session,
082: classOfAccount);
083: // comboBox.setFont(parent.getFont());
084:
085: accountControl.addSelectionListener(new SelectionAdapter() {
086: @Override
087: public void widgetDefaultSelected(SelectionEvent e) {
088: handleDefaultSelection(e);
089:
090: // Needed?
091: applyEditorValueAndDeactivate();
092: }
093: });
094:
095: accountControl.addKeyListener(new KeyAdapter() {
096: // hook key pressed - see PR 14201
097: @Override
098: public void keyPressed(KeyEvent e) {
099: keyReleaseOccured(e);
100: // TODO: Is this code needed?
101:
102: // as a result of processing the above call, clients may have
103: // disposed this cell editor
104: if ((getControl() == null) || getControl().isDisposed()) {
105: return;
106: }
107: checkAccountSelected(); // see explaination below
108: }
109: });
110:
111: accountControl.addTraverseListener(new TraverseListener() {
112: public void keyTraversed(TraverseEvent e) {
113: if (e.detail == SWT.TRAVERSE_ESCAPE
114: || e.detail == SWT.TRAVERSE_RETURN) {
115: e.doit = false;
116: }
117: }
118: });
119:
120: // We really want a selection listener but it is not supported so we
121: // use a key listener and a mouse listener to know when selection changes
122: // may have occured
123: accountControl.addMouseListener(new MouseAdapter() {
124: @Override
125: public void mouseUp(MouseEvent e) {
126: checkAccountSelected();
127: }
128: });
129:
130: accountControl.addFocusListener(new FocusAdapter() {
131: @Override
132: public void focusLost(FocusEvent e) {
133: AccountCellEditor.this .focusLost();
134: }
135: });
136:
137: accountControl.setFont(parent.getFont());
138: accountControl.setBackground(parent.getBackground());
139:
140: // TODO: Do we need these?
141: // text.setText("");//$NON-NLS-1$
142: // text.addModifyListener(getModifyListener());
143:
144: return accountControl;
145: }
146:
147: /**
148: * The <code>TextCellEditor</code> implementation of
149: * this <code>CellEditor</code> framework method returns
150: * the text string.
151: *
152: * @return the text string
153: */
154: @Override
155: protected Object doGetValue() {
156: return accountControl.getAccount();
157: }
158:
159: /* (non-Javadoc)
160: * Method declared on CellEditor.
161: */
162: @Override
163: protected void doSetFocus() {
164: if (accountControl != null) {
165: // text.selectAll();
166: accountControl.setFocus();
167: checkAccountSelected(); // only if text
168: }
169: }
170:
171: /**
172: * The <code>TextCellEditor</code> implementation of
173: * this <code>CellEditor</code> framework method accepts
174: * a text string (type <code>String</code>).
175: *
176: * @param value a text string (type <code>String</code>)
177: */
178: @Override
179: protected void doSetValue(Object value) {
180: Assert.isTrue(accountControl != null);
181: Assert.isTrue(value == null
182: || classOfAccount.isAssignableFrom(value.getClass()));
183:
184: // Listener was removed for text but not combo?
185: // accountControl.removeModifyListener(getModifyListener());
186: accountControl.setAccount(classOfAccount.cast(value));
187: // accountControl.addModifyListener(getModifyListener());
188: }
189:
190: /**
191: * Processes a modify event that occurred in this text cell editor.
192: * This framework method performs validation and sets the error message
193: * accordingly, and then reports a change via <code>fireEditorValueChanged</code>.
194: * Subclasses should call this method at appropriate times. Subclasses
195: * may extend or reimplement.
196: *
197: * @param e the SWT modify event
198: */
199: protected void editOccured(ModifyEvent e) {
200: /* TODO: Figure out this code - it came from the text cell editor
201: String value = text.getText();
202: if (value == null) {
203: value = "";//$NON-NLS-1$
204: }
205: Object typedValue = value;
206: boolean oldValidState = isValueValid();
207: boolean newValidState = isCorrect(typedValue);
208: if (typedValue == null && newValidState) {
209: Assert.isTrue(false,
210: "Validator isn't limiting the cell editor's type range");//$NON-NLS-1$
211: }
212: if (!newValidState) {
213: // try to insert the current value into the error message.
214: setErrorMessage(MessageFormat.format(getErrorMessage(),
215: new Object[] { value }));
216: }
217: valueChanged(oldValidState, newValidState);
218: */
219: }
220:
221: /**
222: * Since a text editor field is scrollable we don't
223: * set a minimumSize.
224: */
225: @Override
226: public LayoutData getLayoutData() {
227: // Text did this:
228: // return new LayoutData();
229:
230: /*
231: * The <code>ComboBoxCellEditor</code> implementation of
232: * this <code>CellEditor</code> framework method sets the
233: * minimum width of the cell. The minimum width is 10 characters
234: * if <code>comboBox</code> is not <code>null</code> or <code>disposed</code>
235: * eles it is 60 pixels to make sure the arrow button and some text is visible.
236: * The list of CCombo will be wide enough to show its longest item.
237: */
238: LayoutData layoutData = super .getLayoutData();
239: if ((accountControl == null) || accountControl.isDisposed()) {
240: layoutData.minimumWidth = 60;
241: } else {
242: // make the comboBox 10 characters wide
243: GC gc = new GC(accountControl);
244: layoutData.minimumWidth = (gc.getFontMetrics()
245: .getAverageCharWidth() * 10) + 10;
246: gc.dispose();
247: }
248: return layoutData;
249:
250: // We could also do this:
251: // return super.getLayoutData();
252: }
253:
254: /**
255: * Return the modify listener.
256: */
257: private ModifyListener getModifyListener() {
258: if (modifyListener == null) {
259: modifyListener = new ModifyListener() {
260: public void modifyText(ModifyEvent e) {
261: editOccured(e);
262: }
263: };
264: }
265: return modifyListener;
266: }
267:
268: /**
269: * Handles a default selection event from the text control by applying the editor
270: * value and deactivating this cell editor.
271: *
272: * @param event the selection event
273: *
274: * @since 3.0
275: */
276: protected void handleDefaultSelection(SelectionEvent event) {
277: // same with enter-key handling code in keyReleaseOccured(e);
278: fireApplyEditorValue();
279: deactivate();
280: }
281:
282: protected boolean isAccountSelected() {
283: return (accountControl != null && accountControl.getAccount() != null);
284: }
285:
286: /**
287: * The <code>TextCellEditor</code> implementation of this
288: * <code>CellEditor</code> method returns <code>true</code> if
289: * the current account is not null.
290: */
291: @Override
292: public boolean isCopyEnabled() {
293: return isAccountSelected();
294: }
295:
296: /**
297: * The <code>AccountCellEditor</code> implementation of this
298: * <code>CellEditor</code> method returns <code>true</code> if
299: * the current account is not null.
300: */
301: @Override
302: public boolean isCutEnabled() {
303: return isAccountSelected();
304: }
305:
306: /**
307: * The <code>AccountCellEditor</code> implementation of this
308: * <code>CellEditor</code> method returns <code>true</code>
309: * in all cases. This allows the account to be set to null
310: * using the delete key.
311: */
312: @Override
313: public boolean isDeleteEnabled() {
314: return isAccountSelected();
315: }
316:
317: /**
318: * The <code>TextCellEditor</code> implementation of this
319: * <code>CellEditor</code> method always returns <code>true</code>.
320: */
321: @Override
322: public boolean isPasteEnabled() {
323: return true;
324: }
325:
326: /**
327: * Processes a key release event that occurred in this cell editor.
328: *
329: * @param keyEvent the key event
330: */
331: @Override
332: protected void keyReleaseOccured(KeyEvent keyEvent) {
333: /* No idea if any of this is needed.....
334: switch (mode) {
335: /*
336: * The <code>TextCellEditor</code> implementation of this framework method
337: * ignores when the RETURN key is pressed since this is handled in
338: * <code>handleDefaultSelection</code>.
339: * An exception is made for Ctrl+Enter for multi-line texts, since
340: * a default selection event is not sent in this case.
341: * /
342: case textmode:
343:
344: if (keyEvent.character == '\r') { // Return key
345: // Enter is handled in handleDefaultSelection.
346: // Do not apply the editor value in response to an Enter key event
347: // since this can be received from the IME when the intent is -not-
348: // to apply the value.
349: // See bug 39074 [CellEditors] [DBCS] canna input mode fires bogus event from Text Control
350: //
351: // An exception is made for Ctrl+Enter for multi-line texts, since
352: // a default selection event is not sent in this case.
353: if (text != null && !text.isDisposed()
354: && (text.getStyle() & SWT.MULTI) != 0) {
355: if ((keyEvent.stateMask & SWT.CTRL) != 0) {
356: super.keyReleaseOccured(keyEvent);
357: }
358: }
359: return;
360: }
361: super.keyReleaseOccured(keyEvent);
362: break;
363:
364: case combo:
365: if (keyEvent.character == '\u001b') { // Escape character
366: fireCancelEditor();
367: } else if (keyEvent.character == '\t') { // tab key
368: applyEditorValueAndDeactivate();
369: }
370: break;
371:
372: default:
373: super.keyReleaseOccured(keyEvent);
374: }
375: */
376: super .keyReleaseOccured(keyEvent);
377: }
378:
379: /**
380: * The <code>TextCellEditor</code> implementation of this
381: * <code>CellEditor</code> method copies the full account
382: * name to the clipboard.
383: */
384: @Override
385: public void performCopy() {
386: // TODO: Implement this
387: }
388:
389: /**
390: * The <code>TextCellEditor</code> implementation of this
391: * <code>CellEditor</code> method copies the full account
392: * name to the clipboard and sets the account to null.
393: */
394: @Override
395: public void performCut() {
396: // TODO: Implement this
397:
398: // Do we need these?
399: checkAccountSelected();
400: }
401:
402: /**
403: * The <code>AccountCellEditor</code> implementation of this
404: * <code>CellEditor</code> method deletes the
405: * account entirely from the control. The value in this
406: * editor becomes a null account reference.
407: */
408: @Override
409: public void performDelete() {
410: accountControl.setAccount(null);
411:
412: // Do we need these?
413: checkAccountSelected();
414: }
415:
416: /**
417: * The <code>AccountCellEditor</code> implementation of this
418: * <code>CellEditor</code> method pastes the
419: * the clipboard contents into the control, completely replacing
420: * what was there before.
421: *
422: * If the text was a full account name then that account will become
423: * the value set in this editor. As the full account name is always
424: * put into the clipboard on a cut or paste, this ensures that accounts
425: * can be copied or moved to other controls correctly.
426: *
427: * If the text is not the full account name of a valid account then the
428: * list is searched for matches.
429: */
430: @Override
431: public void performPaste() {
432: // TODO: Implement this
433:
434: // Do we need these?
435: checkAccountSelected();
436: }
437:
438: // Methods if combobox
439:
440: /**
441: * Applies the currently selected value and deactiavates the cell editor
442: */
443: void applyEditorValueAndDeactivate() {
444: // must set the selection before getting value
445: /* TODO: Figure out this code - it came from the combo
446: selection = comboBox.getSelectionIndex();
447: Object newValue = doGetValue();
448: markDirty();
449: boolean isValid = isCorrect(newValue);
450: setValueValid(isValid);
451:
452: if (!isValid) {
453: // Only format if the 'index' is valid
454: if (items.length > 0 && selection >= 0 && selection < items.length) {
455: // try to insert the current value into the error message.
456: setErrorMessage(MessageFormat.format(getErrorMessage(),
457: new Object[] { items[selection] }));
458: }
459: else {
460: // Since we don't have a valid index, assume we're using an 'edit'
461: // combo so format using its text value
462: setErrorMessage(MessageFormat.format(getErrorMessage(),
463: new Object[] { comboBox.getText() }));
464: }
465: }
466: */
467: fireApplyEditorValue();
468: deactivate();
469: }
470:
471: /*
472: * (non-Javadoc)
473: * @see org.eclipse.jface.viewers.CellEditor#focusLost()
474: */
475: @Override
476: protected void focusLost() {
477: // This was in the combo code only:
478: if (isActivated()) {
479: applyEditorValueAndDeactivate();
480: }
481:
482: // Otherwise this was called
483: super.focusLost();
484: }
485: }
|