Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
View | Details | Raw Unified | Return to bug 106199
Collapse All | Expand All

(-)src/org/eclipse/jface/dialogs/taskassistance/FieldDecoration.java (+67 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2005 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
10
 ******************************************************************************/
11
package org.eclipse.jface.dialogs.taskassistance;
12
13
import org.eclipse.swt.graphics.Image;
14
15
/**
16
 * Simple data structure class for specifying a decoration for a field.
17
 * 
18
 * <p>
19
 * This API is considered experimental. It is still evolving during 3.2 and is
20
 * subject to change. It is being released to obtain feedback from early
21
 * adopters.
22
 * 
23
 * @since 3.2
24
 */
25
public class FieldDecoration {
26
27
	private Image image;
28
29
	private String description;
30
31
	/**
32
	 * Create a decoration for a field with the specified image and description
33
	 * text.
34
	 * 
35
	 * @param image
36
	 *            the image shown in the decoration.
37
	 * @param description
38
	 *            the description shown when the user hovers over the
39
	 *            decoration.
40
	 */
41
	public FieldDecoration(Image image, String description) {
42
		this.image = image;
43
		this.description = description;
44
	}
45
46
	/**
47
	 * Return the image shown in the decoration, or <code>null</code> if no
48
	 * image is specified.
49
	 * 
50
	 * @return the image shown in the decoration. A return value of
51
	 *         <code>null</code> signifies a blank decoration.
52
	 */
53
	public Image getImage() {
54
		return image;
55
	}
56
57
	/**
58
	 * Return the description for the decoration shown when the user hovers over
59
	 * the decoration.
60
	 * 
61
	 * @return the String description of the decoration. A return value of
62
	 *         <code>null</code> indicates that no description will be shown.
63
	 */
64
	public String getDescription() {
65
		return description;
66
	}
67
}
(-)src/org/eclipse/jface/dialogs/taskassistance/ContentProposalPopup.java (+527 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2005 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
10
 ******************************************************************************/
11
package org.eclipse.jface.dialogs.taskassistance;
12
13
import org.eclipse.core.runtime.Assert;
14
import org.eclipse.jface.dialogs.Dialog;
15
import org.eclipse.jface.dialogs.PopupDialog;
16
import org.eclipse.jface.viewers.ILabelProvider;
17
import org.eclipse.swt.SWT;
18
import org.eclipse.swt.events.SelectionEvent;
19
import org.eclipse.swt.events.SelectionListener;
20
import org.eclipse.swt.graphics.FontMetrics;
21
import org.eclipse.swt.graphics.GC;
22
import org.eclipse.swt.graphics.Image;
23
import org.eclipse.swt.graphics.Point;
24
import org.eclipse.swt.graphics.Rectangle;
25
import org.eclipse.swt.layout.GridData;
26
import org.eclipse.swt.widgets.Composite;
27
import org.eclipse.swt.widgets.Control;
28
import org.eclipse.swt.widgets.Event;
29
import org.eclipse.swt.widgets.Listener;
30
import org.eclipse.swt.widgets.Shell;
31
import org.eclipse.swt.widgets.Table;
32
import org.eclipse.swt.widgets.TableItem;
33
import org.eclipse.swt.widgets.Text;
34
35
/**
36
 * A lightweight popup used to show content proposals for a text field. If
37
 * additional information exists for a proposal, then selecting that proposal
38
 * will result in the information being displayed in a secondary popup.
39
 * 
40
 * <p>
41
 * This API is considered experimental. It is still evolving during 3.2 and is
42
 * subject to change. It is being released to obtain feedback from early
43
 * adopters. For now, there is limited API to control how the popup is sized or
44
 * placed relative to the control, or whether it is resizable. The intention is
45
 * that standard techniques will be implemented to "do the right thing"
46
 * depending on the associated control's size/location and the content size, and
47
 * API added if we discover there are additional choices to be made.
48
 * 
49
 * @since 3.2
50
 */
51
public class ContentProposalPopup extends PopupDialog {
52
53
	/**
54
	 * Set to <code>true</code> to use a Table with SWT.VIRTUAL. This is a
55
	 * workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=98585#c40
56
	 * The corresponding SWT bug is
57
	 * https://bugs.eclipse.org/bugs/show_bug.cgi?id=90321
58
	 */
59
	private static final boolean USE_VIRTUAL = !"motif".equals(SWT.getPlatform()); //$NON-NLS-1$
60
61
	/**
62
	 * The delay before showing a secondary popup.
63
	 */
64
	private static final int POPUP_DELAY = 500;
65
	
66
	/**
67
	 * The width hint for the popup and secondary popup.
68
	 */
69
	private static final int POPUP_CHARWIDTH = 20;
70
71
	/*
72
	 * The listener we will install on the target control.
73
	 */
74
	private final class TargetControlListener implements Listener {
75
		// Key events from the control
76
		public void handleEvent(Event e) {
77
			if (!isValid())
78
				return;
79
80
			if (!(e.type == SWT.KeyDown || e.type == SWT.Traverse))
81
				return;
82
83
			// Traverse events will be ignored, but we will interpret their
84
			// characters.
85
			if (e.type == SWT.Traverse) {
86
				e.detail = SWT.TRAVERSE_NONE;
87
				e.doit = false;
88
			}
89
90
			char key = e.character;
91
			if (key == 0) {
92
				int newSelection = proposalTable.getSelectionIndex();
93
				int visibleRows = (proposalTable.getSize().y / proposalTable
94
						.getItemHeight()) - 1;
95
				switch (e.keyCode) {
96
				case SWT.ARROW_UP:
97
					newSelection -= 1;
98
					if (newSelection < 0)
99
						newSelection = proposalTable.getItemCount() - 1;
100
					break;
101
102
				case SWT.ARROW_DOWN:
103
					newSelection += 1;
104
					if (newSelection > proposalTable.getItemCount() - 1)
105
						newSelection = 0;
106
					break;
107
108
				case SWT.PAGE_DOWN:
109
					newSelection += visibleRows;
110
					if (newSelection >= proposalTable.getItemCount())
111
						newSelection = proposalTable.getItemCount() - 1;
112
					break;
113
114
				case SWT.PAGE_UP:
115
					newSelection -= visibleRows;
116
					if (newSelection < 0)
117
						newSelection = 0;
118
					break;
119
120
				case SWT.HOME:
121
					newSelection = 0;
122
					break;
123
124
				case SWT.END:
125
					newSelection = proposalTable.getItemCount() - 1;
126
					break;
127
				default:
128
					if (e.keyCode != SWT.CAPS_LOCK && e.keyCode != SWT.MOD1
129
							&& e.keyCode != SWT.MOD2 && e.keyCode != SWT.MOD3
130
							&& e.keyCode != SWT.MOD4)
131
						close();
132
					return;
133
				}
134
135
				if (newSelection >= 0)
136
					selectProposal(newSelection);
137
				e.doit = false;
138
			}
139
140
			// key != 0
141
			switch (key) {
142
			case SWT.ESC: // Esc
143
				e.doit = false;
144
				close();
145
				break;
146
147
			case SWT.LF:
148
			case SWT.CR:
149
				e.doit = false;
150
				Object p = getSelectedProposal();
151
				if (p != null)
152
					adapter.proposalAccepted(getString(p));
153
				close();
154
				break;
155
156
			case SWT.TAB:
157
				e.doit = false;
158
				getShell().setFocus();
159
				return;
160
161
			default:
162
				break;
163
			}
164
		}
165
	}
166
167
	private class InfoPopupDialog extends PopupDialog {
168
169
		private static final String EMPTY = ""; //$NON-NLS-1$
170
171
		private Text text;
172
173
		private String contents = EMPTY;
174
175
		InfoPopupDialog(Shell parent) {
176
			super(parent, PopupDialog.HOVER_SHELLSTYLE, false, false, false,
177
					false, null, null);
178
		}
179
180
		/*
181
		 * Create a text control for showing the info about a proposal.
182
		 */
183
		protected Control createDialogArea(Composite parent) {
184
			text = new Text(parent, SWT.MULTI | SWT.READ_ONLY | SWT.WRAP);
185
			GridData gd = new GridData(GridData.BEGINNING | GridData.FILL_BOTH);
186
			gd.horizontalIndent = PopupDialog.POPUP_HORIZONTALSPACING;
187
			gd.verticalIndent = PopupDialog.POPUP_VERTICALSPACING;
188
			text.setLayoutData(gd);
189
			text.setText(contents);
190
			return text;
191
		}
192
193
		/*
194
		 * Adjust the bounds to appear next to our parent shell
195
		 */
196
		protected void adjustBounds() {
197
			Rectangle parentBounds = getParentShell().getBounds();
198
			getShell().setBounds(
199
					new Rectangle(parentBounds.x + parentBounds.width + PopupDialog.POPUP_HORIZONTALSPACING,
200
							parentBounds.y + PopupDialog.POPUP_VERTICALSPACING,
201
							parentBounds.width, parentBounds.height));
202
203
		}
204
205
		void setContents(String newContents) {
206
			if (newContents == null)
207
				newContents = EMPTY;
208
			this.contents = newContents;
209
			if (text != null && !text.isDisposed()) {
210
				text.setText(contents);
211
			}
212
		}
213
	}
214
215
	/*
216
	 * The adapter on the target control.
217
	 */
218
	private ContentProposalAdapter adapter;
219
220
	/*
221
	 * The listener installed on the target control.
222
	 */
223
	private Listener controlListener;
224
225
	/*
226
	 * The control on which we are proposing content.
227
	 */
228
	private Control control;
229
230
	/*
231
	 * The table used to show the list of proposals.
232
	 */
233
	private Table proposalTable;
234
235
	/*
236
	 * The provider of proposals and proposal descriptions
237
	 */
238
	private IContentProposalProvider proposalProvider;
239
240
	/*
241
	 * The proposals to be shown (cached to avoid repeated requests).
242
	 */
243
	private Object[] proposals;
244
245
	/*
246
	 * A label provider
247
	 */
248
	private ILabelProvider labelProvider;
249
250
	/*
251
	 * Secondary popup used to show detailed information.
252
	 */
253
	private InfoPopupDialog infoPopup;
254
255
	/**
256
	 * Constructs a new instance of this popup, specifying the control for which
257
	 * this popup is showing content, and describing how the popup should be
258
	 * sized and placed.
259
	 * 
260
	 * @param adapter
261
	 *            the content proposal adapter that opened this dialog.
262
	 * @param control
263
	 *            the control whose content is being proposed.
264
	 * @param proposalProvider
265
	 *            the provider of content proposals
266
	 * @param infoText
267
	 *            Text to be shown in a lower info area, or <code>null</code>
268
	 *            if there is no info area.
269
	 * @param labelProvider
270
	 *            the <code>ILabelProvider</code> used to obtain visual
271
	 *            content for the proposals. Must never be <code>null</code>.
272
	 */
273
	public ContentProposalPopup(ContentProposalAdapter adapter,
274
			Control control, IContentProposalProvider proposalProvider,
275
			String infoText, ILabelProvider labelProvider) {
276
		super(control.getShell(), PopupDialog.INFOPOPUPRESIZE_SHELLSTYLE,
277
				false, false, false, false, null, infoText);
278
		this.adapter = adapter;
279
		this.control = control;
280
		this.labelProvider = labelProvider;
281
		this.proposalProvider = proposalProvider;
282
		this.proposals = getProposals(adapter.getControlContent());
283
	}
284
285
	/**
286
	 * Creates the content area for the proposal popup. This creates a table and
287
	 * places it inside the composite. The table will contain a list of all the
288
	 * proposals.
289
	 * 
290
	 * @param parent
291
	 *            The parent composite to contain the dialog area; must not be
292
	 *            <code>null</code>.
293
	 */
294
	protected final Control createDialogArea(final Composite parent) {
295
		if (USE_VIRTUAL) {
296
			proposalTable = new Table(parent, SWT.H_SCROLL | SWT.V_SCROLL
297
					| SWT.VIRTUAL);
298
299
			Listener listener = new Listener() {
300
				public void handleEvent(Event event) {
301
					handleSetData(event);
302
				}
303
			};
304
			proposalTable.addListener(SWT.SetData, listener);
305
		} else {
306
			proposalTable = new Table(parent, SWT.H_SCROLL | SWT.V_SCROLL);
307
		}
308
		// set the proposals to force population of the table.
309
		setProposals(proposals);
310
311
		proposalTable.setHeaderVisible(false);
312
		proposalTable.addSelectionListener(new SelectionListener() {
313
314
			public void widgetSelected(SelectionEvent e) {
315
			}
316
317
			public void widgetDefaultSelected(SelectionEvent e) {
318
				adapter.proposalAccepted(getString(getSelectedProposal()));
319
			}
320
		});
321
322
		GridData data = new GridData(GridData.FILL_BOTH);
323
		GC gc = new GC(proposalTable);
324
		gc.setFont(proposalTable.getFont());
325
		FontMetrics fontMetrics = gc.getFontMetrics();
326
		gc.dispose();
327
		data.widthHint = Dialog.convertWidthInCharsToPixels(fontMetrics, POPUP_CHARWIDTH);
328
		proposalTable.setLayoutData(data);
329
		return proposalTable;
330
	}
331
332
	/*
333
	 * (non-Javadoc)
334
	 * 
335
	 * @see org.eclipse.jface.dialogs.PopupDialog.adjustBounds()
336
	 */
337
	protected void adjustBounds() {
338
		// Get our control's location in display coordinates.
339
		Point location = control.getDisplay().map(control.getParent(), null,
340
				control.getLocation());
341
		getShell().setLocation(location.x + 3,
342
				location.y + control.getSize().y + 3);
343
	}
344
345
	/**
346
	 * Set the content of the table to show the current proposals.
347
	 * 
348
	 * @param event
349
	 */
350
	private void handleSetData(Event event) {
351
		TableItem item = (TableItem) event.item;
352
		int index = proposalTable.indexOf(item);
353
354
		if (0 <= index && index < proposals.length) {
355
			Object current = proposals[index];
356
			item.setText(getString(current));
357
			item.setImage(getImage(current));
358
			item.setData(current);
359
		} else {
360
			// this should not happen, but does on win32
361
		}
362
	}
363
364
	/**
365
	 * Initializes the proposal popup with these given proposals.
366
	 * 
367
	 * @param proposals
368
	 *            the proposals
369
	 */
370
	private void setProposals(Object[] proposals) {
371
		if (proposals == null || proposals.length == 0) {
372
			proposals = getEmptyProposalArray();
373
		}
374
375
		if (isValid()) {
376
			final int newSize = proposals.length;
377
			if (USE_VIRTUAL) {
378
				proposalTable.setItemCount(newSize);
379
				proposalTable.clearAll();
380
			} else {
381
				proposalTable.setRedraw(false);
382
				proposalTable.setItemCount(newSize);
383
				TableItem[] items = proposalTable.getItems();
384
				for (int i = 0; i < items.length; i++) {
385
					TableItem item = items[i];
386
					Object proposal = proposals[i];
387
					item.setText(getString(proposal));
388
					item.setImage(getImage(proposal));
389
					item.setData(proposal);
390
				}
391
				proposalTable.setRedraw(true);
392
			}
393
			proposalTable.setSelection(0);
394
			proposalTable.showSelection();
395
		}
396
	}
397
398
	private String getString(Object proposal) {
399
		if (proposal == null)
400
			return "Invalid proposal"; //$NON-NLS-1$
401
		if (labelProvider == null)
402
			return proposal.toString();
403
		return labelProvider.getText(proposal);
404
	}
405
406
	private Image getImage(Object proposal) {
407
		if (proposal == null || labelProvider == null)
408
			return null;
409
		return labelProvider.getImage(proposal);
410
	}
411
412
	private Object[] getEmptyProposalArray() {
413
		return new Object[0];
414
	}
415
416
	private void addControlListener() {
417
		controlListener = new TargetControlListener();
418
		control.addListener(SWT.KeyDown, controlListener);
419
		control.addListener(SWT.Traverse, controlListener);
420
	}
421
422
	private void removeControlListener() {
423
		control.removeListener(SWT.KeyDown, controlListener);
424
		control.removeListener(SWT.Traverse, controlListener);
425
	}
426
427
	private boolean isValid() {
428
		return proposalTable != null && !proposalTable.isDisposed();
429
	}
430
431
	/**
432
	 * Return the current selected proposal.
433
	 */
434
	private Object getSelectedProposal() {
435
		if (isValid()) {
436
			int i = proposalTable.getSelectionIndex();
437
			if (proposals == null || i < 0 || i >= proposals.length)
438
				return null;
439
			return proposals[i];
440
		}
441
		return null;
442
	}
443
444
	/**
445
	 * Select the proposal at the given index.
446
	 */
447
	private void selectProposal(int index) {
448
		Assert.isTrue(index >= 0, "Proposal index should never be negative"); //$NON-NLS-1$
449
		if (!isValid() || proposals == null || index >= proposals.length)
450
			return;
451
		proposalTable.setSelection(index);
452
		proposalTable.showSelection();
453
454
		String description = getProposalDescription(proposals[index]);
455
		showProposalDescription(description);
456
	}
457
458
	/**
459
	 * Opens this window, creating it first if it has not yet been created.
460
	 * <p>
461
	 * This method is extended in order to add the control listener when the popup
462
	 * is opened and to invoke the secondary popup if applicable.
463
	 * 
464
	 * @return the return code
465
	 * 
466
	 * @see org.eclipse.jface.window.Window#open()
467
	 */
468
	public int open() {
469
		addControlListener();
470
		int value =  super.open();
471
		showProposalDescription(getProposalDescription(getSelectedProposal()));
472
		return value;
473
	}
474
475
	/**
476
	 * Closes this window, disposes its shell, and removes this window from its
477
	 * window manager (if it has one).
478
	 * <p>
479
	 * This method is extended to remove the control listener.
480
	 * </p>
481
	 * 
482
	 * @return <code>true</code> if the window is (or was already) closed, and
483
	 *         <code>false</code> if it is still open
484
	 */
485
	public boolean close() {
486
		removeControlListener();
487
		return super.close();
488
	}
489
490
	private Object[] getProposals(String contents) {
491
		if (proposalProvider == null)
492
			return null;
493
		if (proposalProvider instanceof IFilteringContentProposalProvider)
494
			return ((IFilteringContentProposalProvider)proposalProvider).getProposals(contents);
495
		return proposalProvider.getProposals();
496
	}
497
498
	private String getProposalDescription(Object proposal) {
499
		if (proposalProvider == null)
500
			return null;
501
		return proposalProvider.getProposalDescription(proposal);
502
	}
503
504
	private void showProposalDescription(String description) {
505
		if (infoPopup == null && description != null) {
506
			infoPopup = new InfoPopupDialog(getShell());
507
			infoPopup.setContents(description);
508
			Runnable runnable = new Runnable() {
509
				public void run() {
510
					try {
511
						Thread.sleep(POPUP_DELAY);
512
					} catch (InterruptedException e) {
513
					}
514
					getShell().getDisplay().syncExec(new Runnable() {
515
						public void run() {
516
							infoPopup.open();
517
						}
518
					});
519
				}
520
			};
521
			Thread t = new Thread(runnable);
522
			t.start();
523
		} else if (infoPopup != null) {
524
			infoPopup.setContents(description);
525
		}
526
	}
527
}
(-)src/org/eclipse/jface/dialogs/taskassistance/IContentProposalListener.java (+28 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2005 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
10
 *******************************************************************************/
11
package org.eclipse.jface.dialogs.taskassistance;
12
13
/**
14
 * <p>
15
 * This interface is used to listen to notifications from a
16
 * ContentProposalAdapter.
17
 * 
18
 * @since 3.2
19
 */
20
public interface IContentProposalListener {
21
	/**
22
	 * A content proposal has been accepted.
23
	 * 
24
	 * @param proposal
25
	 *            the proposed content string.
26
	 */
27
	public void proposalAccepted(String proposal);
28
}
(-)src/org/eclipse/jface/dialogs/taskassistance/IControlCreator.java (+41 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2005 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
10
 *******************************************************************************/
11
package org.eclipse.jface.dialogs.taskassistance;
12
13
import org.eclipse.swt.widgets.Composite;
14
import org.eclipse.swt.widgets.Control;
15
16
/**
17
 * This interface is used to create a control with a specific parent and style
18
 * bits. It is used by DecoratedFields to create a specific kind of control.
19
 * Clients are expected to implement this interface in order to create a
20
 * particular kind of control for decoration.
21
 * 
22
 * <p>
23
 * This API is considered experimental. It is still evolving during 3.2 and is
24
 * subject to change. It is being released to obtain feedback from early
25
 * adopters.
26
 * 
27
 * @since 3.2
28
 */
29
public interface IControlCreator {
30
	/**
31
	 * Create a control with the specified parent and style bits.
32
	 * 
33
	 * @param parent
34
	 *            the parent of the control
35
	 * @param style
36
	 *            the style of the control
37
	 * 
38
	 * @return the Control that was created.
39
	 */
40
	public Control createControl(Composite parent, int style);
41
}
(-)src/org/eclipse/jface/dialogs/taskassistance/ContentProposalAdapter.java (+345 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2005 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
10
 ******************************************************************************/
11
package org.eclipse.jface.dialogs.taskassistance;
12
13
import org.eclipse.core.runtime.ListenerList;
14
import org.eclipse.jface.bindings.keys.KeyStroke;
15
import org.eclipse.jface.util.Assert;
16
import org.eclipse.jface.viewers.ILabelProvider;
17
import org.eclipse.jface.viewers.LabelProvider;
18
import org.eclipse.swt.SWT;
19
import org.eclipse.swt.widgets.Control;
20
import org.eclipse.swt.widgets.Event;
21
import org.eclipse.swt.widgets.Listener;
22
23
/**
24
 * A content proposal adapter can be used to attach content proposal behavior to
25
 * a control. This behavior includes obtaining proposals, opening a popup
26
 * dialog, managing the content of the control relative to the selections in the
27
 * popup, and optionally opening up a secondary popup to further describe
28
 * proposals.
29
 * 
30
 * <p>
31
 * This API is considered experimental. It is still evolving during 3.2 and is
32
 * subject to change. It is being released to obtain feedback from early
33
 * adopters.
34
 * 
35
 * @since 3.2
36
 */
37
public class ContentProposalAdapter {
38
39
	/**
40
	 * Flag that controls the printing of debug info.
41
	 */
42
	public static final boolean DEBUG = false;
43
44
	/**
45
	 * Indicates that a chosen proposal should be inserted into the field.
46
	 */
47
	public static final int PROPOSAL_INSERT = 1;
48
49
	/**
50
	 * Indicates that a chosen proposal should replace the current contents of
51
	 * the field.
52
	 */
53
	public static final int PROPOSAL_REPLACE = 2;
54
55
	/**
56
	 * Indicates that a chosen proposal should be ignored. This is typically
57
	 * used when the client code implements specialized behavior that occurs
58
	 * when a proposal is chosen. In this case, clients typically register an
59
	 * IContentProposalListener so that they are notified when a proposal is
60
	 * chosen.
61
	 */
62
	public static final int PROPOSAL_IGNORE = 3;
63
64
	/*
65
	 * A string containing the characters that auto-trigger content assist.
66
	 */
67
68
	private IContentProposalProvider proposalProvider;
69
70
	private ILabelProvider labelProvider;
71
72
	private Control control;
73
74
	private IControlContentAdapter controlContentAdapter;
75
76
	private ContentProposalPopup popup;
77
78
	private KeyStroke triggerKeyStroke;
79
80
	private int acceptance;
81
82
	private boolean propagateKeyStroke;
83
84
	private Listener controlListener;
85
86
	private ListenerList proposalListeners = new ListenerList();
87
88
	/**
89
	 * Construct a content proposal adapter that can assist the user with
90
	 * choosing content for the field.
91
	 * 
92
	 * @param control
93
	 *            the control for which the adapter is providing content assist.
94
	 * @param controlContentAdapter
95
	 *            the <code>IControlContentAdapter</code> used to obtain and
96
	 *            update the control's contents as proposals are accepted.
97
	 * @param proposalProvider
98
	 *            the <code>IContentProposalProvider</code> used to obtain
99
	 *            content proposals for this control, or <code>null</code> if
100
	 *            no content proposal is available.
101
	 * @param labelProvider
102
	 *            the label provider which provides text and image information
103
	 *            for content proposals. A <code>null</code> value indicates
104
	 *            that a default label provider is sufficient for any content
105
	 *            proposals that may occur.
106
	 * @param keyStroke
107
	 *            the keystroke that will invoke the content proposal popup.
108
	 * @param propagateKeyStroke
109
	 *            a boolean that indicates whether the triggering keystroke
110
	 *            should be propagated to the adapted control.
111
	 * @param acceptance
112
	 *            a constant indicating how an accepted proposal should affect
113
	 *            the control's content. Should be one of
114
	 *            <code>PROPOSAL_INSERT</code>, <code>PROPOSAL_REPLACE</code>,
115
	 *            or <code>PROPOSAL_IGNORE</code>
116
	 * 
117
	 */
118
	public ContentProposalAdapter(Control control,
119
			IControlContentAdapter controlContentAdapter,
120
			IContentProposalProvider proposalProvider,
121
			ILabelProvider labelProvider, KeyStroke keyStroke,
122
			boolean propagateKeyStroke, int acceptance) {
123
		super();
124
		this.control = control;
125
		this.controlContentAdapter = controlContentAdapter;
126
		this.proposalProvider = proposalProvider;
127
		this.labelProvider = labelProvider;
128
		this.triggerKeyStroke = keyStroke;
129
		this.propagateKeyStroke = propagateKeyStroke;
130
		this.acceptance = acceptance;
131
		addControlListener(control);
132
	}
133
134
	/**
135
	 * Get the control on which the content proposal adapter is installed.
136
	 * 
137
	 * @return the control on which the proposal adapter is installed.
138
	 */
139
	public Control getControl() {
140
		return control;
141
	}
142
143
	/**
144
	 * Get the label provider that is used to show proposals. A default label
145
	 * provider will be used if one has not been set.
146
	 * 
147
	 * @return the <code>ILabelProvider</code> used to show proposals
148
	 */
149
	public ILabelProvider getLabelProvider() {
150
		if (labelProvider == null) {
151
			labelProvider = new LabelProvider();
152
		}
153
		return labelProvider;
154
	}
155
156
	/**
157
	 * Set the label provider that is used to show proposals.
158
	 * 
159
	 * @param labelProvider
160
	 *            the <code>ILabelProvider</code> used to show proposals.
161
	 */
162
	public void setLabelProvider(ILabelProvider labelProvider) {
163
		this.labelProvider = labelProvider;
164
	}
165
166
	/**
167
	 * Get the proposal provider that provides content proposals given the
168
	 * current content of the field. A value of <code>null</code> indicates
169
	 * that there are no content proposals available for the field.
170
	 * 
171
	 * @return the <code>IContentProposalProvider</code> used to show
172
	 *         proposals. May be <code>null</code>.
173
	 */
174
	public IContentProposalProvider getContentProposalProvider() {
175
		return proposalProvider;
176
	}
177
178
	/**
179
	 * Set the content proposal provider that is used to show proposals.
180
	 * 
181
	 * @param proposalProvider
182
	 *            the <code>IContentProposalProvider</code> used to show
183
	 *            proposals
184
	 */
185
	public void setContentProposalProvider(
186
			IContentProposalProvider proposalProvider) {
187
		this.proposalProvider = proposalProvider;
188
	}
189
190
	/**
191
	 * Add the specified listener to the list of content proposal listeners that
192
	 * are notified when content proposals are chosen.
193
	 * </p>
194
	 * 
195
	 * @param listener
196
	 *            the IContentProposalListener to be added as a listener. Must
197
	 *            not be <code>null</code>. If an attempt is made to register
198
	 *            an instance which is already registered with this instance,
199
	 *            this method has no effect.
200
	 * 
201
	 * @see org.eclipse.jface.dialogs.taskassistance.IContentProposalListener
202
	 */
203
	public void addContentProposalListener(IContentProposalListener listener) {
204
		proposalListeners.add(listener);
205
	}
206
207
	private void addControlListener(Control control) {
208
		if (DEBUG)
209
			System.out
210
					.println("ContentProposalListener#installControlListener()"); //$NON-NLS-1$
211
212
		if (controlListener != null)
213
			return;
214
		controlListener = new Listener() {
215
			public void handleEvent(Event e) {
216
				switch (e.type) {
217
				case SWT.KeyDown:
218
					if (DEBUG)
219
						dump("keyDown OK", e); //$NON-NLS-1$
220
					if (triggerKeyStroke != null) {
221
						// Either there are no modifiers for the trigger and we
222
						// check the character field...
223
						if ((triggerKeyStroke.getModifierKeys() == KeyStroke.NO_KEY && triggerKeyStroke
224
								.getNaturalKey() == e.character)
225
								||
226
								// ...or there are modifiers, in which case the
227
								// keycode and state must match
228
								(triggerKeyStroke.getNaturalKey() == e.keyCode && ((triggerKeyStroke
229
										.getModifierKeys() & e.stateMask) == triggerKeyStroke
230
										.getModifierKeys()))) {
231
							e.doit = propagateKeyStroke;
232
							openProposalPopup();
233
234
						}
235
					}
236
					break;
237
238
				default:
239
					Assert.isTrue(false);
240
				}
241
			}
242
243
			/**
244
			 * Dump the given events to "standard" output.
245
			 * 
246
			 * @param who
247
			 *            who dump's
248
			 * @param e
249
			 *            the event
250
			 */
251
			private void dump(String who, Event e) {
252
				StringBuffer sb = new StringBuffer(
253
						"--- [ContentProposalAdapter]\n"); //$NON-NLS-1$
254
				sb.append(who);
255
				sb.append(" - e: keyCode=" + e.keyCode + hex(e.keyCode)); //$NON-NLS-1$
256
				sb.append("; character=" + e.character + hex(e.character)); //$NON-NLS-1$
257
				sb.append("; stateMask=" + e.stateMask + hex(e.stateMask)); //$NON-NLS-1$
258
				sb.append("; doit=" + e.doit); //$NON-NLS-1$
259
				sb.append("; detail=" + e.detail + hex(e.detail)); //$NON-NLS-1$
260
				sb.append("; widget=" + e.widget); //$NON-NLS-1$
261
				System.out.println(sb);
262
			}
263
264
			private String hex(int i) {
265
				return "[0x" + Integer.toHexString(i) + ']'; //$NON-NLS-1$
266
			}
267
		};
268
		control.addListener(SWT.KeyDown, controlListener);
269
270
		if (DEBUG)
271
			System.out
272
					.println("ContentProposalAdapter#installControlListener() - installed"); //$NON-NLS-1$
273
	}
274
275
	private void openProposalPopup() {
276
		if (popup == null) {
277
			popup = new ContentProposalPopup(this, control, proposalProvider,
278
					null, labelProvider);
279
		}
280
		popup.open();
281
	}
282
283
	/**
284
	 * A content proposal has been accepted. Update the control contents
285
	 * accordingly and notify any listeners.
286
	 * 
287
	 * @param text
288
	 *            the text that was accepted as the proposal.
289
	 */
290
	public void proposalAccepted(String text) {
291
		switch (acceptance) {
292
		case (PROPOSAL_REPLACE):
293
			setControlContent(text);
294
			break;
295
		case (PROPOSAL_INSERT):
296
			insertControlContent(text);
297
			break;
298
		default:
299
			// do nothing. Typically a listener is installed to handle this in
300
			// a custom way.
301
			break;
302
		}
303
304
		// In all cases, notify listeners of an accepted proposal.
305
		final Object[] listenerArray = proposalListeners.getListeners();
306
		for (int i = 0; i < listenerArray.length; i++)
307
			((IContentProposalListener) listenerArray[i])
308
					.proposalAccepted(text);
309
	}
310
311
	/**
312
	 * Return the text content of the control.
313
	 * 
314
	 * @return the String contents of the control. Never <code>null</code>.
315
	 */
316
	public String getControlContent() {
317
		if (isValid())
318
			return controlContentAdapter.getControlContents(control);
319
		return ""; //$NON-NLS-1$
320
	}
321
322
	/*
323
	 * Set the text content of the control
324
	 */
325
	private void setControlContent(String text) {
326
		if (isValid())
327
			controlContentAdapter.setControlContents(control, text);
328
	}
329
330
	/*
331
	 * Insert the specified text into the control content.
332
	 */
333
	private void insertControlContent(String text) {
334
		if (isValid())
335
			controlContentAdapter.insertControlContents(control, text);
336
	}
337
338
	/*
339
	 * Check that the control and content adapter are valid.
340
	 */
341
	private boolean isValid() {
342
		return control != null && !control.isDisposed()
343
				&& controlContentAdapter != null;
344
	}
345
}
(-)src/org/eclipse/jface/dialogs/taskassistance/IControlContentAdapter.java (+59 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2005 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
10
 *******************************************************************************/
11
package org.eclipse.jface.dialogs.taskassistance;
12
13
import org.eclipse.swt.widgets.Control;
14
15
/**
16
 * This interface is used to set and retrieve text content from an arbitrary
17
 * control. Clients are expected to implement this interface when defining a
18
 * ContentProposalAdapter, in order to specify how to retrieve and set the
19
 * contents of the control being adapted.
20
 * 
21
 * <p>
22
 * This API is considered experimental. It is still evolving during 3.2 and is
23
 * subject to change. It is being released to obtain feedback from early
24
 * adopters.
25
 * 
26
 * @since 3.2
27
 */
28
public interface IControlContentAdapter {
29
	/**
30
	 * Set the contents of the specified control to the specified text. Must not
31
	 * be <code>null</code>.
32
	 * 
33
	 * @param control
34
	 *            the control whose contents are to be set (replaced).
35
	 * @param contents
36
	 *            the String specifying the new control content.
37
	 */
38
	public void setControlContents(Control control, String contents);
39
40
	/**
41
	 * Insert the specified contents into the control's current contents. Must
42
	 * not be <code>null</code>.
43
	 * 
44
	 * @param control
45
	 *            the control whose contents are to be altered.
46
	 * @param contents
47
	 *            the String to be inserted into the control contents.
48
	 */
49
	public void insertControlContents(Control control, String contents);
50
51
	/**
52
	 * Get the text contents of the control.
53
	 * 
54
	 * @param control
55
	 *            the control whose contents are to be retrieved.
56
	 * @return the String contents of the control.
57
	 */
58
	public String getControlContents(Control control);
59
}
(-)src/org/eclipse/jface/dialogs/taskassistance/SimpleContentProposalProvider.java (+75 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2005 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
10
 *******************************************************************************/
11
package org.eclipse.jface.dialogs.taskassistance;
12
13
/**
14
 * The SimpleContentProposalProvider is a class designed to return a static list
15
 * of items when queried for content proposals.
16
 * <p>
17
 * This API is considered experimental. It is still evolving during 3.2 and is
18
 * subject to change. It is being released to obtain feedback from early
19
 * adopters.
20
 * 
21
 * @since 3.2
22
 * 
23
 */
24
public class SimpleContentProposalProvider implements IContentProposalProvider {
25
26
	// The proposals to display
27
	private Object[] proposals;
28
29
	/**
30
	 * Construct a SimpleContentProposalProvider whose content proposals always
31
	 * consist of the specified array of Objects.
32
	 * 
33
	 * @param proposals
34
	 *            the array of Objects to be returned whenever proposals are
35
	 *            requested.
36
	 */
37
	public SimpleContentProposalProvider(Object[] proposals) {
38
		super();
39
		this.proposals = proposals;
40
	}
41
42
	/**
43
	 * Return an array of Objects representing the valid content proposals for a
44
	 * field.
45
	 * 
46
	 * @return the array of Objects that represent valid proposals for the field
47
	 *         given its current content.
48
	 */
49
	public Object[] getProposals() {
50
		return this.proposals;
51
	}
52
53
	/**
54
	 * Set the proposals to be returned by the receiver whenever content
55
	 * proposals are requested.
56
	 * 
57
	 * @param items
58
	 *            Object[]
59
	 */
60
	public void setProposals(Object[] items) {
61
		this.proposals = items;
62
	}
63
64
	/**
65
	 * Return a String that describes the given proposal in more detail.
66
	 * 
67
	 * @param proposal
68
	 *            the Object representing a valid proposal
69
	 * @return return <code>null</code> to indicate that there is no
70
	 *         description available.
71
	 */
72
	public String getProposalDescription(Object proposal) {
73
		return null;
74
	}
75
}
(-)src/org/eclipse/jface/dialogs/taskassistance/package.html (+31 lines)
Added Link Here
1
<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
2
<html>
3
<head>
4
   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
5
   <meta name="Author" content="IBM">
6
   <meta name="GENERATOR" content="Mozilla/4.5 [en] (Win98; I) [Netscape]">
7
   <title>Package-level Javadoc</title>
8
</head>
9
<body>
10
Provides support for classes that help communicate proper content for dialogs and wizards.
11
<h2>
12
Package Specification</h2>
13
<p>
14
This package provides several classes that help users complete the content in a dialog
15
or wizard.  <tt>DecoratedField</tt> can be used to decorate controls with images that
16
communicate information about the control's content.  Decorations can be
17
used to show additional information about a field, such as its status, or a cue
18
that shows availability of content proposals.  Decorated fields handle the creation
19
of the field's control, since a specialized layout is used to manage the decoration.
20
Otherwise, clients are expected to interact directly with the control.  Decorations can 
21
optionally show descriptive text when the user hovers over them.  
22
</p>
23
<p>
24
A <tt>ContentProposalAdapter</tt> can be attached to a control to provide content
25
proposals.  These proposals are typically shown in a popup dialog as the user types information
26
into a field, or when a special assist key is invoked.  The <tt>ContentProposalAdapter</tt>
27
manages the creation, hiding, and showing of the proposal popup.  
28
</p>
29
<p>Note: None of the classes in this package maintain global state.
30
</body>
31
</html>
(-)src/org/eclipse/jface/dialogs/taskassistance/TextControlCreator.java (+33 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2005 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
10
 ******************************************************************************/
11
package org.eclipse.jface.dialogs.taskassistance;
12
13
import org.eclipse.swt.widgets.Composite;
14
import org.eclipse.swt.widgets.Control;
15
import org.eclipse.swt.widgets.Text;
16
17
/**
18
 * A Text control creator. Convenience class to create text controls to
19
 * be supplied to a decorated field.
20
 * 
21
 * <p>
22
 * This API is considered experimental. It is still evolving during 3.2 and is
23
 * subject to change. It is being released to obtain feedback from early
24
 * adopters.
25
 * 
26
 * @since 3.2
27
 */
28
public class TextControlCreator implements IControlCreator {
29
30
	public Control createControl(Composite parent, int style) {
31
		return new Text(parent, style);
32
	}
33
}
(-)src/org/eclipse/jface/dialogs/taskassistance/IFilteringContentProposalProvider.java (+39 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2005 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
10
 ******************************************************************************/
11
package org.eclipse.jface.dialogs.taskassistance;
12
13
/**
14
 * An IFilteringProposalProvider provides a list of objects that are appropriate
15
 * for a textual dialog field, given the field's current content.  
16
 * 
17
 * <p>
18
 * This API is considered experimental. It is still evolving during 3.2 and is
19
 * subject to change. It is being released to obtain feedback from early
20
 * adopters.
21
 * 
22
 * @since 3.2
23
 */
24
public interface IFilteringContentProposalProvider extends IContentProposalProvider {
25
26
	/**
27
	 * Return an array of Objects representing the valid content proposals for a
28
	 * field when it contains the given text content.
29
	 * 
30
	 * @param content
31
	 *            the String representing the current value of the field for
32
	 *            which proposals are requested.
33
	 * @return the Objects that represent valid proposals for the field given
34
	 *         its current content. The proposal may be a String, or an Object
35
	 *         that will be mapped to a text value using an associated
36
	 *         <code>org.eclipse.jface.viewers.ILabelProvider</code>.
37
	 */
38
	Object[] getProposals(String content);
39
}
(-)src/org/eclipse/jface/dialogs/taskassistance/DecoratedField.java (+655 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2005 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
10
 ******************************************************************************/
11
package org.eclipse.jface.dialogs.taskassistance;
12
13
import org.eclipse.core.runtime.Assert;
14
import org.eclipse.jface.dialogs.Dialog;
15
import org.eclipse.jface.dialogs.IDialogConstants;
16
import org.eclipse.swt.SWT;
17
import org.eclipse.swt.events.DisposeEvent;
18
import org.eclipse.swt.events.DisposeListener;
19
import org.eclipse.swt.events.FocusEvent;
20
import org.eclipse.swt.events.FocusListener;
21
import org.eclipse.swt.events.MouseAdapter;
22
import org.eclipse.swt.events.MouseEvent;
23
import org.eclipse.swt.events.MouseTrackListener;
24
import org.eclipse.swt.events.PaintEvent;
25
import org.eclipse.swt.events.PaintListener;
26
import org.eclipse.swt.graphics.FontMetrics;
27
import org.eclipse.swt.graphics.GC;
28
import org.eclipse.swt.graphics.Point;
29
import org.eclipse.swt.graphics.Region;
30
import org.eclipse.swt.layout.FormAttachment;
31
import org.eclipse.swt.layout.FormData;
32
import org.eclipse.swt.layout.FormLayout;
33
import org.eclipse.swt.layout.GridData;
34
import org.eclipse.swt.widgets.Composite;
35
import org.eclipse.swt.widgets.Control;
36
import org.eclipse.swt.widgets.Display;
37
import org.eclipse.swt.widgets.Label;
38
import org.eclipse.swt.widgets.Shell;
39
40
/**
41
 * A decorated field manages image decorations around a control. It allows
42
 * clients to specify an image decoration and a position for the decoration
43
 * relative to the field. Decorations may be assigned descriptions, which are
44
 * shown when the user hovers over the decoration. Clients can decorate any kind
45
 * of control by supplying an <code>IControlCreator</code> to create the
46
 * control that is decorated.
47
 * 
48
 * <p>
49
 * This API is considered experimental. It is still evolving during 3.2 and is
50
 * subject to change. It is being released to obtain feedback from early
51
 * adopters.
52
 * 
53
 * @since 3.2
54
 */
55
public class DecoratedField {
56
57
	/**
58
	 * Number of pixels to reserve for decorations.
59
	 */
60
	private static int RESERVED_WIDTH = 8;
61
62
	/**
63
	 * Cached platform flags for dealing with platform-specific issues.
64
	 */
65
	private static boolean CARBON = "carbon".equals(SWT.getPlatform()); //$NON-NLS-1$
66
67
	/**
68
	 * Constants describing the array indices used to hold the decorations in
69
	 * array slots.
70
	 */
71
72
	private static final int LEFT_TOP = 0;
73
74
	private static final int LEFT_BOTTOM = 1;
75
76
	private static final int RIGHT_TOP = 2;
77
78
	private static final int RIGHT_BOTTOM = 3;
79
80
	private static final int DECORATION_SLOTS = 4;
81
82
	/**
83
	 * Set the width (in pixels) that should always be reserved for field
84
	 * decorations, regardless of the actual width of any supplied decorations.
85
	 * Field alignment within dialogs will look best when all decorations
86
	 * supplied conform to the reserved width. However, the field decoration
87
	 * area for a particular field will be expanded if decorations larger than
88
	 * the reserved width are supplied.
89
	 * 
90
	 * @param decorationWidth
91
	 */
92
	public static void setReservedDecorationWidth(int decorationWidth) {
93
		RESERVED_WIDTH = decorationWidth;
94
	}
95
96
	/**
97
	 * Simple data structure class for specifying the internals for a field
98
	 * decoration. This class contains data specific to the implementation of
99
	 * field decorations as labels attached to the field. Clients should use
100
	 * <code>FieldDecoration</code> for specifying a decoration.
101
	 */
102
	private class FieldDecorationData {
103
104
		/* Package */FieldDecoration decoration;
105
106
		/* Package */Label label;
107
108
		/* Package */FormData data;
109
110
		/* Package */boolean showOnFocus;
111
112
		/* Package */boolean visible = true;
113
114
		/**
115
		 * Create a decoration data representing the specified decoration, using
116
		 * the specified label and form data for its representation.
117
		 * 
118
		 * @param decoration
119
		 *            the decoration whose data is kept.
120
		 * @param label
121
		 *            the label used to represent the decoration.
122
		 * @param formData
123
		 *            the form data used to attach the decoration to its field.
124
		 * @param showOnFocus
125
		 *            a boolean specifying whether the decoration should only be
126
		 *            shown when the field has focus.
127
		 */
128
		FieldDecorationData(FieldDecoration decoration, Label label,
129
				FormData formData, boolean showOnFocus) {
130
			this.decoration = decoration;
131
			this.label = label;
132
			this.data = formData;
133
			this.showOnFocus = showOnFocus;
134
		}
135
	}
136
137
	/**
138
	 * Decorations keyed by position.
139
	 */
140
	private FieldDecorationData[] decDatas = new FieldDecorationData[DECORATION_SLOTS];
141
142
	/**
143
	 * The associated control
144
	 */
145
	private Control control;
146
147
	/**
148
	 * The composite with form layout used to manage decorations.
149
	 */
150
	private Composite form;
151
152
	/**
153
	 * The hover used for showing description text
154
	 */
155
	private Hover hover;
156
157
	/**
158
	 * The hover used to show a decoration image's description.
159
	 */
160
	class Hover {
161
		/**
162
		 * Offset of info hover arrow from the left or right side.
163
		 */
164
		private int a_OFFSET = 10;
165
166
		/**
167
		 * Width of info hover arrow.
168
		 */
169
		private int a_WIDTH = 8;
170
171
		/**
172
		 * Height of info hover arrow.
173
		 */
174
		private int a_HEIGHT = 10;
175
176
		/**
177
		 * Margin around info hover text.
178
		 */
179
		private int h_MARGIN = 2;
180
181
		/**
182
		 * This info hover's shell.
183
		 */
184
		Shell hoverShell;
185
186
		/**
187
		 * The info hover text.
188
		 */
189
		String text = ""; //$NON-NLS-1$
190
191
		Hover(Shell parent) {
192
			final Display display = parent.getDisplay();
193
			hoverShell = new Shell(parent, SWT.NO_TRIM | SWT.ON_TOP
194
					| SWT.NO_FOCUS);
195
			hoverShell.setBackground(display
196
					.getSystemColor(SWT.COLOR_INFO_BACKGROUND));
197
			hoverShell.setForeground(display
198
					.getSystemColor(SWT.COLOR_INFO_FOREGROUND));
199
			hoverShell.addPaintListener(new PaintListener() {
200
				public void paintControl(PaintEvent pe) {
201
					pe.gc.drawString(text, h_MARGIN, h_MARGIN);
202
					if (!CARBON)
203
						pe.gc.drawPolygon(getPolygon(true));
204
				}
205
			});
206
			hoverShell.addMouseListener(new MouseAdapter() {
207
				public void mouseDown(MouseEvent e) {
208
					hideHover();
209
				}
210
			});
211
		}
212
213
		int[] getPolygon(boolean border) {
214
			Point e = getExtent();
215
			if (border)
216
				return new int[] { 0, 0, e.x - 1, 0, e.x - 1, e.y - 1,
217
						a_OFFSET + a_WIDTH, e.y - 1, a_OFFSET + a_WIDTH / 2,
218
						e.y + a_HEIGHT - 1, a_OFFSET, e.y - 1, 0, e.y - 1, 0, 0 };
219
			return new int[] { 0, 0, e.x, 0, e.x, e.y, a_OFFSET + a_WIDTH, e.y,
220
					a_OFFSET + a_WIDTH / 2, e.y + a_HEIGHT, a_OFFSET, e.y, 0,
221
					e.y, 0, 0 };
222
		}
223
224
		void dispose() {
225
			if (!hoverShell.isDisposed())
226
				hoverShell.dispose();
227
		}
228
229
		void setVisible(boolean visible) {
230
			if (visible) {
231
				if (!hoverShell.isVisible())
232
					hoverShell.setVisible(true);
233
			} else {
234
				if (hoverShell.isVisible())
235
					hoverShell.setVisible(false);
236
			}
237
		}
238
239
		void setText(String t) {
240
			if (t == null)
241
				t = ""; //$NON-NLS-1$
242
			if (!t.equals(text)) {
243
				Point oldSize = getExtent();
244
				text = t;
245
				hoverShell.redraw();
246
				Point newSize = getExtent();
247
				if (!oldSize.equals(newSize)) {
248
					Region region = new Region();
249
					region.add(getPolygon(false));
250
					hoverShell.setRegion(region);
251
				}
252
			}
253
		}
254
255
		boolean isVisible() {
256
			return hoverShell.isVisible();
257
		}
258
259
		void setLocation(Control control) {
260
			if (control != null) {
261
				int h = getExtent().y;
262
				hoverShell.setLocation(control.toDisplay(-a_OFFSET + a_WIDTH
263
						/ 2, -h - a_HEIGHT + 1));
264
			}
265
		}
266
267
		Point getExtent() {
268
			GC gc = new GC(hoverShell);
269
			Point e = gc.textExtent(text);
270
			gc.dispose();
271
			e.x += h_MARGIN * 2;
272
			e.y += h_MARGIN * 2;
273
			return e;
274
		}
275
	}
276
277
	/**
278
	 * Construct a decorated field which is parented by the specified composite
279
	 * and has the given style bits. Use the controlCreator to create the
280
	 * specific kind of control that is decorated inside the field.
281
	 * 
282
	 * @param parent
283
	 *            the parent of the decorated field.
284
	 * @param style
285
	 *            the desired style bits for the field.
286
	 * @param controlCreator
287
	 *            the IControlCreator used to specify the specific kind of
288
	 *            control that is to be decorated.
289
	 * 
290
	 * @see IControlCreator
291
	 */
292
	public DecoratedField(Composite parent, int style,
293
			IControlCreator controlCreator) {
294
		this.form = createForm(parent);
295
		this.control = controlCreator.createControl(form, style);
296
297
		addControlListeners();
298
		// Add a dummy decoration on each side to reserve the width needed.
299
		addFieldDecoration(new FieldDecoration(null, null), SWT.LEFT | SWT.TOP,
300
				true);
301
		addFieldDecoration(new FieldDecoration(null, null),
302
				SWT.RIGHT | SWT.TOP, true);
303
		form.setTabList(new Control[] { control });
304
305
		// Set up the preferred width of the control and attachments to the
306
		// decorations.
307
		FormData data = new FormData();
308
		GC gc = new GC(control);
309
		gc.setFont(control.getFont());
310
		FontMetrics fontMetrics = gc.getFontMetrics();
311
		gc.dispose();
312
		data.width = Dialog.convertHorizontalDLUsToPixels(fontMetrics,
313
				IDialogConstants.ENTRY_FIELD_WIDTH)
314
				+ 2 * RESERVED_WIDTH;
315
		data.left = new FormAttachment(decDatas[LEFT_TOP].label);
316
		data.right = new FormAttachment(decDatas[RIGHT_TOP].label);
317
		data.top = new FormAttachment(0, 0);
318
		control.setLayoutData(data);
319
320
	}
321
322
	/**
323
	 * Adds an image decoration to the field.
324
	 * 
325
	 * @param decoration
326
	 *            A FieldDecoration describing the image and description for the
327
	 *            decoration
328
	 * 
329
	 * @param position
330
	 *            The SWT constant indicating the position of the decoration
331
	 *            relative to the field's control. The position should include
332
	 *            style bits describing both the vertical and horizontal
333
	 *            orientation. <code>SWT.LEFT</code> and
334
	 *            <code>SWT.RIGHT</code> describe the horizontal placement of
335
	 *            the decoration relative to the field, and the constants
336
	 *            <code>SWT.TOP</code> and <code>SWT.BOTTOM</code> describe
337
	 *            the vertical alignment of the decoration relative to the
338
	 *            field. Decorations always appear on either horizontal side of
339
	 *            the field, never above or below it. For example, a decoration
340
	 *            appearing on the left side of the field, at the top, is
341
	 *            specified as SWT.LEFT | SWT.TOP. If an image decoration
342
	 *            already exists in the specified position, it will be replaced
343
	 *            by the one specified.
344
	 * @param showOnFocus
345
	 *            <code>true</code> if the decoration should only be shown
346
	 *            when the associated control has focus, <code>false</code> if
347
	 *            it should always be shown.
348
	 * 
349
	 */
350
	public void addFieldDecoration(FieldDecoration decoration, int position,
351
			boolean showOnFocus) {
352
		final Label label;
353
		FormData data;
354
		int i = indexForPosition(position);
355
		if (decDatas[i] == null) {
356
			data = createFormDataForIndex(i);
357
			label = new Label(form, SWT.HORIZONTAL | SWT.VERTICAL | SWT.CENTER);
358
			label.addMouseTrackListener(new MouseTrackListener() {
359
				public void mouseHover(MouseEvent event) {
360
					FieldDecorationData decData = (FieldDecorationData) event.widget
361
							.getData();
362
					String desc = decData.decoration.getDescription();
363
					if (desc != null) {
364
						showHoverText(desc, label);
365
					}
366
				}
367
368
				public void mouseEnter(MouseEvent event) {
369
				}
370
371
				public void mouseExit(MouseEvent event) {
372
					hideHover();
373
				}
374
			});
375
			decDatas[i] = new FieldDecorationData(decoration, label, data,
376
					showOnFocus);
377
		} else {
378
			label = decDatas[i].label;
379
			data = decDatas[i].data;
380
			decDatas[i].decoration = decoration;
381
			decDatas[i].showOnFocus = showOnFocus;
382
		}
383
		label.setImage(decDatas[i].decoration.getImage());
384
		label.setData(decDatas[i]);
385
		label.setVisible(!showOnFocus);
386
		label.setLayoutData(data);
387
	}
388
389
	/**
390
	 * Get the control that is decorated by the receiver.
391
	 * 
392
	 * @return the Control decorated by the receiver, or <code>null</code> if
393
	 *         none has been created yet.
394
	 */
395
	public Control getControl() {
396
		return control;
397
	}
398
399
	/**
400
	 * Get the control that represents the decorated field. This composite
401
	 * should be used to lay out the field within its parent.
402
	 * 
403
	 * @return the Control that should be layed out in the field's parent's
404
	 *         layout. This is typically not the control itself, since
405
	 *         additional controls are used to represent the decorations.
406
	 */
407
	public Control getLayoutControl() {
408
		return form;
409
	}
410
411
	/**
412
	 * Create the parent composite and a form layout that will be used to manage
413
	 * decorations.
414
	 */
415
	private Composite createForm(Composite parent) {
416
		Composite composite = new Composite(parent, SWT.NO_FOCUS);
417
		composite.setLayout(new FormLayout());
418
		composite.setLayoutData(new GridData(SWT.BEGINNING, SWT.BEGINNING,
419
				true, true));
420
		return composite;
421
	}
422
423
	/**
424
	 * Add any listeners needed on the control.
425
	 */
426
	private void addControlListeners() {
427
		control.addDisposeListener(new DisposeListener() {
428
			public void widgetDisposed(DisposeEvent event) {
429
				if (hover != null)
430
					hover.dispose();
431
			}
432
		});
433
		control.addFocusListener(new FocusListener() {
434
			public void focusGained(FocusEvent event) {
435
				controlFocusGained();
436
			}
437
438
			public void focusLost(FocusEvent event) {
439
				controlFocusLost();
440
			}
441
442
		});
443
	}
444
445
	/**
446
	 * Return the index in the array of decoration datas that represents the
447
	 * specified SWT position.
448
	 * 
449
	 * @param position
450
	 *            The SWT constant indicating the position of the decoration
451
	 *            relative to the field's control. The position should include
452
	 *            style bits describing both the vertical and horizontal
453
	 *            orientation. <code>SWT.LEFT</code> and
454
	 *            <code>SWT.RIGHT</code> describe the horizontal placement of
455
	 *            the decoration relative to the field, and the constants
456
	 *            <code>SWT.TOP</code> and <code>SWT.BOTTOM</code> describe
457
	 *            the vertical alignment of the decoration relative to the
458
	 *            field. Decorations always appear on either horizontal side of
459
	 *            the field, never above or below it. For example, a decoration
460
	 *            appearing on the left side of the field, at the top, is
461
	 *            specified as SWT.LEFT | SWT.TOP. If an image decoration
462
	 *            already exists in the specified position, it will be replaced
463
	 *            by the one specified.
464
	 * 
465
	 * @return index the index in the array of decorations that represents the
466
	 *         specified SWT position. If the position is not an expected
467
	 *         position, the index representing the top left position will be
468
	 *         returned.
469
	 * 
470
	 */
471
	int indexForPosition(int position) {
472
		switch (position) {
473
		case SWT.LEFT | SWT.BOTTOM:
474
			return LEFT_BOTTOM;
475
		case SWT.RIGHT | SWT.TOP:
476
			return RIGHT_TOP;
477
		case SWT.RIGHT | SWT.BOTTOM:
478
			return RIGHT_BOTTOM;
479
		default:
480
			return LEFT_TOP;
481
		}
482
	}
483
484
	/**
485
	 * Create a form data that will place the decoration at the specified
486
	 * position.
487
	 * 
488
	 * @param position
489
	 *            The SWT constant indicating the position of the decoration
490
	 *            relative to the field's control. The position should include
491
	 *            style bits describing both the vertical and horizontal
492
	 *            orientation. <code>SWT.LEFT</code> and
493
	 *            <code>SWT.RIGHT</code> describe the horizontal placement of
494
	 *            the decoration relative to the field, and the constants
495
	 *            <code>SWT.TOP</code> and <code>SWT.BOTTOM</code> describe
496
	 *            the vertical alignment of the decoration relative to the
497
	 *            field. Decorations always appear on either horizontal side of
498
	 *            the field, never above or below it. For example, a decoration
499
	 *            appearing on the left side of the field, at the top, is
500
	 *            specified as SWT.LEFT | SWT.TOP. If an image decoration
501
	 *            already exists in the specified position, it will be replaced
502
	 *            by the one specified.
503
	 * 
504
	 */
505
	private FormData createFormDataForIndex(int index) {
506
		Assert.isTrue(index >= 0 && index < DECORATION_SLOTS,
507
				"Index out of range"); //$NON-NLS-1$
508
509
		FormData data = new FormData();
510
		data.width = RESERVED_WIDTH;
511
		switch (index) {
512
		case LEFT_TOP:
513
			data.left = new FormAttachment(0, 0);
514
			data.top = new FormAttachment(0, 0);
515
			return data;
516
		case LEFT_BOTTOM:
517
			data.left = new FormAttachment(0, 0);
518
			data.bottom = new FormAttachment(100, 0);
519
			return data;
520
		case RIGHT_TOP:
521
			data.right = new FormAttachment(100, 0);
522
			data.top = new FormAttachment(0, 0);
523
			return data;
524
		case RIGHT_BOTTOM:
525
			data.right = new FormAttachment(100, 0);
526
			data.bottom = new FormAttachment(100, 0);
527
			return data;
528
		}
529
		// should never get here, making compiler happy
530
		return data;
531
	}
532
533
	/**
534
	 * Show the specified text using the same hover dialog as is used to show
535
	 * decorator descriptions. Normally, a decoration's description text will be
536
	 * shown in an info hover over the field's control whenever the mouse hovers
537
	 * over the decoration. This method can be used to show a decoration's
538
	 * description text at other times (such as when the control receives
539
	 * focus), or to show other text associated with the field.
540
	 * 
541
	 * <p>
542
	 * If there is currently a hover visible, the hover's text will be replaced
543
	 * with the specified text.
544
	 * 
545
	 * @param text
546
	 *            the text to be shown in the info hover, or <code>null</code>
547
	 *            if no text should be shown.
548
	 */
549
	public void showHoverText(String text) {
550
		showHoverText(text, control);
551
	}
552
553
	/**
554
	 * Hide any hover popups that are currently showing on the control.
555
	 * Normally, a decoration's description text will be shown in an info hover
556
	 * over the field's control as long as the mouse hovers over the decoration,
557
	 * and will be hidden when the mouse exits the control. This method can be
558
	 * used to hide a hover that was shown using <code>showHoverText</code>,
559
	 * or to programatically hide the current decoration hover.
560
	 * 
561
	 * <p>
562
	 * This message has no effect if there is no current hover.
563
	 * 
564
	 */
565
	public void hideHover() {
566
		if (hover != null) {
567
			hover.setVisible(false);
568
		}
569
	}
570
571
	private void controlFocusGained() {
572
		for (int i = 0; i < DECORATION_SLOTS; i++) {
573
			if (decDatas[i] != null && decDatas[i].showOnFocus)
574
				setVisible(decDatas[i], true);
575
		}
576
	}
577
578
	private void controlFocusLost() {
579
		for (int i = 0; i < DECORATION_SLOTS; i++) {
580
			if (decDatas[i] != null && decDatas[i].showOnFocus)
581
				setVisible(decDatas[i], false);
582
		}
583
	}
584
585
	/**
586
	 * Show the specified decoration. This message has no effect if the
587
	 * decoration is already showing, or was not already added to the field
588
	 * using <code>addFieldDecoration</code>.
589
	 * 
590
	 * @param decoration
591
	 *            the decoration to be shown.
592
	 */
593
	public void showDecoration(FieldDecoration decoration) {
594
		FieldDecorationData data = getDecorationData(decoration);
595
		if (data == null)
596
			return;
597
		// record the fact that client would like it to be visible
598
		data.visible = true;
599
		// even if it is supposed to be shown, if the field does not have focus,
600
		// do not show it (yet)
601
		if (!data.showOnFocus || control.isFocusControl())
602
			setVisible(data, true);
603
	}
604
605
	/**
606
	 * Hide the specified decoration. This message has no effect if the
607
	 * decoration is already hidden, or was not already added to the field using
608
	 * <code>addFieldDecoration</code>.
609
	 * 
610
	 * @param decoration
611
	 *            the decoration to be hidden.
612
	 */
613
	public void hideDecoration(FieldDecoration decoration) {
614
		FieldDecorationData data = getDecorationData(decoration);
615
		if (data == null)
616
			return;
617
		// store the visibility in the decoration data so internal changes in
618
		// visibility don't
619
		// accidentally show it.
620
		data.visible = false;
621
		setVisible(data, false);
622
	}
623
624
	private void setVisible(FieldDecorationData decData, boolean visible) {
625
		// Don't set it visible if the client has instructed us to hide it.
626
		if (visible && decData.visible)
627
			decData.label.setVisible(true);
628
		else
629
			decData.label.setVisible(false);
630
	}
631
632
	private FieldDecorationData getDecorationData(FieldDecoration dec) {
633
		for (int i = 0; i < DECORATION_SLOTS; i++) {
634
			if (decDatas[i] != null && dec == decDatas[i].decoration
635
					&& decDatas[i].label != null
636
					&& !decDatas[i].label.isDisposed())
637
				return decDatas[i];
638
		}
639
		return null;
640
	}
641
642
	private void showHoverText(String text, Control hoverNear) {
643
		if (text == null) {
644
			hideHover();
645
			return;
646
		}
647
648
		if (hover == null) {
649
			hover = new Hover(hoverNear.getShell());
650
		}
651
		hover.setText(text);
652
		hover.setLocation(hoverNear);
653
		hover.setVisible(true);
654
	}
655
}
(-)src/org/eclipse/jface/dialogs/taskassistance/TextContentAdapter.java (+53 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2005 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
10
 ******************************************************************************/
11
package org.eclipse.jface.dialogs.taskassistance;
12
13
import org.eclipse.swt.widgets.Control;
14
import org.eclipse.swt.widgets.Text;
15
16
/**
17
 * A content adapter for SWT Text controls. Used to create a ContentProposalAdapter
18
 * for text fields.
19
 * 
20
 * <p>
21
 * This API is considered experimental. It is still evolving during 3.2 and is
22
 * subject to change. It is being released to obtain feedback from early
23
 * adopters.
24
 * 
25
 * @since 3.2
26
 */
27
public class TextContentAdapter implements IControlContentAdapter {
28
29
	/*
30
	 * (non-Javadoc)
31
	 * @see org.eclipse.jface.dialogs.taskassistance.IControlContentAdapter#getControlContents(org.eclipse.swt.widgets.Control)
32
	 */
33
	public String getControlContents(Control control) {
34
		return ((Text)control).getText();
35
	}
36
37
	/*
38
	 * (non-Javadoc)
39
	 * @see org.eclipse.jface.dialogs.taskassistance.IControlContentAdapter#setControlContents(org.eclipse.swt.widgets.Control, java.lang.String)
40
	 */
41
	public void setControlContents(Control control, String text) {
42
		((Text)control).setText(text);
43
		((Text)control).setSelection(text.length(), text.length());
44
	}
45
46
	/*
47
	 * (non-Javadoc)
48
	 * @see org.eclipse.jface.dialogs.taskassistance.IControlContentAdapter#insertControlContents(org.eclipse.swt.widgets.Control, java.lang.String)
49
	 */
50
	public void insertControlContents(Control control, String text) {
51
		((Text)control).insert(text);
52
	}
53
}
(-)src/org/eclipse/jface/dialogs/taskassistance/IContentProposalProvider.java (+48 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2005 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
10
 ******************************************************************************/
11
package org.eclipse.jface.dialogs.taskassistance;
12
13
/**
14
 * An IContentProposalProvider provides a list of objects that are appropriate
15
 * for a textual dialog field, given the field's current content. Proposals may
16
 * optionally provide a description.
17
 * 
18
 * <p>
19
 * This API is considered experimental. It is still evolving during 3.2 and is
20
 * subject to change. It is being released to obtain feedback from early
21
 * adopters.
22
 * 
23
 * @since 3.2
24
 */
25
public interface IContentProposalProvider {
26
27
	/**
28
	 * Return an array of Objects representing the valid content proposals for a
29
	 * field.
30
	 * 
31
	 * @return the Objects that represent valid proposals for the field. The
32
	 *         proposal may be a String, or an Object that will be mapped to a
33
	 *         text value using an associated
34
	 *         <code>org.eclipse.jface.viewers.ILabelProvider</code>.
35
	 */
36
	Object[] getProposals();
37
38
	/**
39
	 * Return a String that describes the given proposal in more detail.
40
	 * 
41
	 * @param proposal
42
	 *            the Object representing a valid proposal
43
	 * @return a String that further describes the proposal, or
44
	 *         <code>null</code> if there is no description available.
45
	 */
46
	String getProposalDescription(Object proposal);
47
48
}

Return to bug 106199