View | Details | Raw Unified | Return to bug 195691 | Differences between
and this patch

Collapse All | Expand All

(-)src/org/eclipse/mylyn/internal/tasks/ui/wizards/ScreenshotAttachmentPage.java (-172 / +348 lines)
Lines 15-28 Link Here
15
import java.util.Map;
15
import java.util.Map;
16
import java.util.Set;
16
import java.util.Set;
17
17
18
import org.eclipse.jface.action.Action;
19
import org.eclipse.jface.action.ActionContributionItem;
20
import org.eclipse.jface.action.IAction;
21
import org.eclipse.jface.action.Separator;
22
import org.eclipse.jface.action.ToolBarManager;
18
import org.eclipse.jface.layout.GridDataFactory;
23
import org.eclipse.jface.layout.GridDataFactory;
24
import org.eclipse.jface.resource.ImageDescriptor;
19
import org.eclipse.jface.wizard.IWizardPage;
25
import org.eclipse.jface.wizard.IWizardPage;
20
import org.eclipse.jface.wizard.WizardPage;
26
import org.eclipse.jface.wizard.WizardPage;
21
import org.eclipse.mylyn.internal.tasks.core.LocalAttachment;
22
import org.eclipse.mylyn.internal.tasks.ui.TaskListColorsAndFonts;
27
import org.eclipse.mylyn.internal.tasks.ui.TaskListColorsAndFonts;
23
import org.eclipse.mylyn.internal.tasks.ui.TasksUiImages;
28
import org.eclipse.mylyn.internal.tasks.ui.TasksUiImages;
24
import org.eclipse.swt.SWT;
29
import org.eclipse.swt.SWT;
25
import org.eclipse.swt.custom.ScrolledComposite;
30
import org.eclipse.swt.custom.ScrolledComposite;
31
import org.eclipse.swt.custom.ViewForm;
26
import org.eclipse.swt.events.ControlAdapter;
32
import org.eclipse.swt.events.ControlAdapter;
27
import org.eclipse.swt.events.ControlEvent;
33
import org.eclipse.swt.events.ControlEvent;
28
import org.eclipse.swt.events.MouseAdapter;
34
import org.eclipse.swt.events.MouseAdapter;
Lines 30-75 Link Here
30
import org.eclipse.swt.events.MouseMoveListener;
36
import org.eclipse.swt.events.MouseMoveListener;
31
import org.eclipse.swt.events.PaintEvent;
37
import org.eclipse.swt.events.PaintEvent;
32
import org.eclipse.swt.events.PaintListener;
38
import org.eclipse.swt.events.PaintListener;
33
import org.eclipse.swt.events.SelectionAdapter;
39
import org.eclipse.swt.graphics.Color;
34
import org.eclipse.swt.events.SelectionEvent;
35
import org.eclipse.swt.graphics.Cursor;
40
import org.eclipse.swt.graphics.Cursor;
36
import org.eclipse.swt.graphics.GC;
41
import org.eclipse.swt.graphics.GC;
37
import org.eclipse.swt.graphics.Image;
42
import org.eclipse.swt.graphics.Image;
38
import org.eclipse.swt.graphics.Point;
43
import org.eclipse.swt.graphics.Point;
44
import org.eclipse.swt.graphics.RGB;
39
import org.eclipse.swt.graphics.Rectangle;
45
import org.eclipse.swt.graphics.Rectangle;
40
import org.eclipse.swt.graphics.Region;
46
import org.eclipse.swt.graphics.Region;
41
import org.eclipse.swt.layout.GridLayout;
42
import org.eclipse.swt.widgets.Button;
43
import org.eclipse.swt.widgets.Canvas;
47
import org.eclipse.swt.widgets.Canvas;
44
import org.eclipse.swt.widgets.Composite;
48
import org.eclipse.swt.widgets.Composite;
45
import org.eclipse.swt.widgets.Display;
49
import org.eclipse.swt.widgets.Display;
50
import org.eclipse.swt.widgets.Event;
46
import org.eclipse.swt.widgets.Shell;
51
import org.eclipse.swt.widgets.Shell;
52
import org.eclipse.swt.widgets.ToolItem;
47
53
48
/**
54
/**
49
 * A wizard page to create a screenshot from the display.
55
 * A wizard page to create a screenshot from the display.
50
 * <p>
51
 * Note: this class uses {@link GC#setXORMode(boolean)} which is deprecated because of lack of Mac implementation
52
 * (bug#50228). The strategy to make it work on platforms XOR is implemented and still have a good looking in platforms
53
 * where it is not implemented, is to draw each line 2 times: first using a WHITE foreground followed by the same draw
54
 * using a BLACK foreground. On platforms where XOR is implemented, the second draw will have no effect, since anything
55
 * XOR black results in noop. On platforms where XOR is NOT implemented, the {@link GC#setXORMode(boolean)} is a noop,
56
 * so the end result will be a black line.
57
 * 
56
 * 
58
 * @author Balazs Brinkus (bug 160572)
57
 * @author Balazs Brinkus (bug 160572)
59
 * @author Mik Kersten
58
 * @author Mik Kersten
60
 * @author Willian Mitsuda
59
 * @author Willian Mitsuda
61
 */
60
 */
62
public class ScreenshotAttachmentPage extends WizardPage {
61
public class ScreenshotAttachmentPage extends WizardPage implements IImageCreator {
63
62
64
	private ScreenshotAttachmentPage page;
63
	private IAction captureAction;
65
64
66
	private LocalAttachment attachment;
65
	private IAction fitAction;
67
66
68
	private Button captureButton;
67
	private IAction cropAction;
69
68
70
	private Button fitButton;
69
	private IAction markAction;
71
70
72
	private Image screenshotImage;
71
	private IAction colorAction;
72
73
	private Image colorIcon;
74
75
	private Color markColor;
76
77
	private IAction clearAction;
78
79
	/**
80
	 * Original screenshot image; used for backup purposes
81
	 */
82
	private Image originalImage;
83
84
	/**
85
	 * Copy of {@link #originalImage original} image; all drawing operations are done here; base for the result image
86
	 */
87
	private Image workImage;
88
89
	/**
90
	 * Used to draw into {@link #workImage}
91
	 */
92
	private GC workImageGC;
73
93
74
	private Canvas canvas;
94
	private Canvas canvas;
75
95
Lines 87-99 Link Here
87
	private Rectangle originalSelection;
107
	private Rectangle originalSelection;
88
108
89
	/**
109
	/**
90
	 * Temporary storage for selection start point or selection resizing initial reference point; this value is
110
	 * Temporary storage for selection start point, selection resizing initial reference point or previous mark point
91
	 * normalized to real image coordinates, no matter the zoom level (see {@link #scaleFactor})
111
	 * (it depends on current tool); this value is normalized to real image coordinates, no matter the zoom level (see
112
	 * {@link #scaleFactor})
92
	 */
113
	 */
93
	private Point startPoint;
114
	private Point startPoint;
94
115
95
	/**
116
	/**
96
	 * What sides I'm resizing when doing an selection {@link Action#RESIZING_SELECTION resize}
117
	 * What sides I'm resizing when doing an selection {@link EditorAction#RESIZING_SELECTION resize}
97
	 */
118
	 */
98
	private Set<SelectionSide> resizableSides = EnumSet.noneOf(SelectionSide.class);
119
	private Set<SelectionSide> resizableSides = EnumSet.noneOf(SelectionSide.class);
99
120
Lines 110-157 Link Here
110
	/**
131
	/**
111
	 * Available actions for the screenshot editor
132
	 * Available actions for the screenshot editor
112
	 */
133
	 */
113
	private static enum Action {
134
	private static enum EditorAction {
114
135
115
		IDLE, SELECTING, RESIZING_SELECTION, MOVING_SELECTION;
136
		CROPPING, SELECTING, RESIZING_SELECTION, MOVING_SELECTION, MARKING;
116
137
117
	};
138
	};
118
139
119
	/**
140
	/**
120
	 * What am I doing now?
141
	 * What am I doing now?
121
	 */
142
	 */
122
	private Action currentAction = Action.IDLE;
143
	private EditorAction currentAction = EditorAction.CROPPING;
123
144
124
	protected ScreenshotAttachmentPage(LocalAttachment attachment) {
145
	protected ScreenshotAttachmentPage() {
125
		super("ScreenShotAttachment");
146
		super("ScreenShotAttachment");
126
		setTitle("Capture Screenshot");
147
		setTitle("Capture Screenshot");
127
		setDescription("After capturing, drag the mouse to crop. This window will not be captured. "
148
		setDescription("After capturing, you can crop the image and make drawings on it. This window will not be captured. "
128
				+ "Note that you can continue to interact with the workbench in order to set up the screenshot.");
149
				+ "Note that you can continue to interact with the workbench in order to set up the screenshot.");
129
		this.attachment = attachment;
130
		this.page = this;
131
	}
150
	}
132
151
133
	public void createControl(Composite parent) {
152
	public void createControl(Composite parent) {
134
153
		ViewForm vf = new ViewForm(parent, SWT.BORDER | SWT.FLAT);
135
		Composite composite = new Composite(parent, SWT.NONE);
154
		vf.horizontalSpacing = 0;
136
		composite.setLayout(new GridLayout());
155
		vf.verticalSpacing = 0;
137
		setControl(composite);
156
		setControl(vf);
157
		vf.setLayoutData(GridDataFactory.fillDefaults().create());
138
158
139
		allocateCursors();
159
		allocateCursors();
140
160
141
		Composite buttonsComposite = new Composite(composite, SWT.NONE);
161
		// TODO: need disabled versions of all toolbar icons
142
		buttonsComposite.setLayout(new GridLayout(3, false));
162
		ToolBarManager tbm = new ToolBarManager(SWT.FLAT | SWT.HORIZONTAL | SWT.RIGHT);
143
		captureButton = new Button(buttonsComposite, SWT.PUSH);
163
		captureAction = new Action("&Capture Desktop", IAction.AS_PUSH_BUTTON) {
144
		captureButton.setText("Capture Desktop");
164
145
		captureButton.setImage(TasksUiImages.getImage(TasksUiImages.IMAGE_CAPTURE));
165
			private boolean isFirstCapture = true;
146
		captureButton.addSelectionListener(new SelectionAdapter() {
147
166
148
			public void widgetSelected(SelectionEvent e) {
167
			@Override
168
			public void run() {
149
				captureScreenshotContent();
169
				captureScreenshotContent();
150
				page.setErrorMessage(null);
170
				setErrorMessage(null);
151
				fitButton.setEnabled(true);
171
				if (isFirstCapture) {
172
					isFirstCapture = false;
173
					fitAction.setEnabled(true);
174
					cropAction.setEnabled(true);
175
					cropAction.setChecked(true);
176
					markAction.setEnabled(true);
177
					clearAction.setEnabled(false);
178
				}
152
			}
179
			}
153
180
154
		});
181
		};
182
		captureAction.setToolTipText("Capture Desktop");
183
		captureAction.setImageDescriptor(ImageDescriptor.createFromImage(TasksUiImages.getImage(TasksUiImages.IMAGE_CAPTURE)));
155
184
156
//		captureDelayedButton = new Button(buttonsComposite, SWT.PUSH);
185
//		captureDelayedButton = new Button(buttonsComposite, SWT.PUSH);
157
//		final String captureIn = "Capture in ";
186
//		final String captureIn = "Capture in ";
Lines 186-239 Link Here
186
//			}
215
//			}
187
//		});
216
//		});
188
217
189
		fitButton = new Button(buttonsComposite, SWT.TOGGLE);
218
		fitAction = new Action("", IAction.AS_CHECK_BOX) {
190
		fitButton.setText("Fit Image");
219
			@Override
191
		fitButton.setImage(TasksUiImages.getImage(TasksUiImages.IMAGE_FIT));
220
			public void run() {
192
		fitButton.addSelectionListener(new SelectionAdapter() {
193
194
			public void widgetSelected(SelectionEvent e) {
195
				refreshCanvasSize();
221
				refreshCanvasSize();
196
			}
222
			}
223
		};
224
		fitAction.setToolTipText("Fit Image");
225
		fitAction.setText("&Fit Image");
226
		fitAction.setImageDescriptor(ImageDescriptor.createFromImage(TasksUiImages.getImage(TasksUiImages.IMAGE_FIT)));
227
		fitAction.setChecked(true);
228
		fitAction.setEnabled(false);
197
229
198
		});
230
		cropAction = new Action("C&rop", IAction.AS_RADIO_BUTTON) {
199
		fitButton.setSelection(true);
231
			@Override
200
		fitButton.setEnabled(false);
232
			public void run() {
233
				currentAction = EditorAction.CROPPING;
234
				cropAction.setChecked(true);
235
				markAction.setChecked(false);
236
				colorAction.setEnabled(false);
237
				canvas.redraw();
238
			}
239
		};
240
		cropAction.setToolTipText("Crop");
241
		cropAction.setImageDescriptor(ImageDescriptor.createFromFile(getClass(), "crop.png"));
242
		cropAction.setEnabled(false);
243
244
		markAction = new Action("&Annotate", IAction.AS_RADIO_BUTTON) {
245
			@Override
246
			public void run() {
247
				currentAction = EditorAction.MARKING;
248
				cropAction.setChecked(false);
249
				markAction.setChecked(true);
250
				colorAction.setEnabled(true);
251
				canvas.redraw();
252
			}
253
		};
254
		markAction.setToolTipText("Draw annotations on screenshot image");
255
		markAction.setImageDescriptor(ImageDescriptor.createFromFile(getClass(), "mark.gif"));
256
		markAction.setDisabledImageDescriptor(ImageDescriptor.createFromFile(getClass(), "mark_disabled.gif"));
257
		markAction.setEnabled(false);
258
259
		colorAction = new Action("", IAction.AS_DROP_DOWN_MENU) {
260
			@Override
261
			public void runWithEvent(final Event e) {
262
				final ColorSelectionWindow colorWindow = new ColorSelectionWindow(getControl().getShell()) {
263
264
					@Override
265
					protected Point getInitialLocation(Point initialSize) {
266
						ToolItem toolItem = (ToolItem) e.widget;
267
						Rectangle itemBounds = toolItem.getBounds();
268
						Point location = toolItem.getParent().toDisplay(itemBounds.x + itemBounds.width,
269
								itemBounds.y + itemBounds.height);
270
						location.x -= initialSize.x;
271
						return location;
272
					}
273
274
				};
275
				colorWindow.setBlockOnOpen(true);
276
				colorWindow.open();
277
				RGB color = colorWindow.getSelectedRGB();
278
				if (color != null) {
279
					setMarkColor(color);
280
				}
281
			}
282
		};
283
		colorAction.setToolTipText("Change pen color");
284
		colorIcon = new Image(getShell().getDisplay(), 16, 16);
285
		setMarkColor(new RGB(255, 85, 85));
286
		colorAction.setEnabled(false);
201
287
202
		scrolledComposite = new ScrolledComposite(composite, SWT.V_SCROLL | SWT.H_SCROLL | SWT.BORDER);
288
		clearAction = new Action("C&lear Annotations", IAction.AS_PUSH_BUTTON) {
203
		scrolledComposite.setLayoutData(GridDataFactory.fillDefaults()
289
			@Override
204
				.align(SWT.FILL, SWT.FILL)
290
			public void run() {
205
				.grab(true, true)
291
				clearAction.setEnabled(false);
206
				.create());
292
				workImageGC.drawImage(originalImage, 0, 0);
293
				canvas.redraw();
294
				markAttachmentDirty();
295
			}
296
		};
297
		clearAction.setToolTipText("Clear all annotations made on screenshot image");
298
		clearAction.setImageDescriptor(ImageDescriptor.createFromFile(getClass(), "erase.png"));
299
		clearAction.setEnabled(false);
300
301
		tbm.add(createAndConfigureCI(captureAction));
302
		tbm.add(createAndConfigureCI(fitAction));
303
		tbm.add(new Separator());
304
		tbm.add(createAndConfigureCI(cropAction));
305
		tbm.add(createAndConfigureCI(markAction));
306
		tbm.add(createAndConfigureCI(colorAction));
307
		tbm.add(new Separator());
308
		tbm.add(createAndConfigureCI(clearAction));
207
309
310
		scrolledComposite = new ScrolledComposite(vf, SWT.V_SCROLL | SWT.H_SCROLL);
208
		canvas = new Canvas(scrolledComposite, SWT.DOUBLE_BUFFERED);
311
		canvas = new Canvas(scrolledComposite, SWT.DOUBLE_BUFFERED);
209
		scrolledComposite.setContent(canvas);
312
		scrolledComposite.setContent(canvas);
210
		canvas.addPaintListener(new PaintListener() {
313
		canvas.addPaintListener(new PaintListener() {
211
314
212
			public void paintControl(PaintEvent e) {
315
			public void paintControl(PaintEvent e) {
213
				if (screenshotImage != null) {
316
				if (workImage != null) {
214
					Rectangle imageBounds = screenshotImage.getBounds();
317
					Rectangle imageBounds = workImage.getBounds();
215
					Rectangle canvasBounds = canvas.getClientArea();
318
					Rectangle canvasBounds = canvas.getClientArea();
216
319
217
					if (fitButton.getSelection()) {
320
					if (fitAction.isChecked()) {
218
						e.gc.drawImage(screenshotImage, 0, 0, imageBounds.width, imageBounds.height, 0, 0,
321
						e.gc.drawImage(workImage, 0, 0, imageBounds.width, imageBounds.height, 0, 0,
219
								canvasBounds.width, canvasBounds.height);
322
								canvasBounds.width, canvasBounds.height);
220
					} else {
323
					} else {
221
						e.gc.drawImage(screenshotImage, 0, 0);
324
						e.gc.drawImage(workImage, 0, 0);
222
					}
223
224
					if (currentAction == Action.IDLE) {
225
						if (currentSelection != null) {
226
							drawSelection(e.gc);
227
						}
228
					} else if (currentAction == Action.SELECTING || currentAction == Action.RESIZING_SELECTION
229
							|| currentAction == Action.MOVING_SELECTION) {
230
						if (currentSelection != null) {
231
							drawSelectionPreview(e.gc);
232
						}
233
					}
325
					}
326
					drawSelection(e.gc);
234
				} else {
327
				} else {
235
//					page.setErrorMessage("Screenshot required");
328
//					page.setErrorMessage("Screenshot required");
236
					fitButton.setEnabled(false);
329
					fitAction.setEnabled(false);
237
				}
330
				}
238
			}
331
			}
239
		});
332
		});
Lines 241-257 Link Here
241
		scrolledComposite.addControlListener(new ControlAdapter() {
334
		scrolledComposite.addControlListener(new ControlAdapter() {
242
			@Override
335
			@Override
243
			public void controlResized(ControlEvent e) {
336
			public void controlResized(ControlEvent e) {
244
				if (fitButton.getSelection()) {
337
				if (fitAction.isChecked()) {
245
					refreshCanvasSize();
338
					refreshCanvasSize();
246
				}
339
				}
247
			}
340
			}
248
		});
341
		});
249
342
343
		vf.setTopLeft(tbm.createControl(vf));
344
		vf.setContent(scrolledComposite);
250
		registerMouseListeners();
345
		registerMouseListeners();
251
	}
346
	}
252
347
348
	private ActionContributionItem createAndConfigureCI(IAction action) {
349
		ActionContributionItem ci = new ActionContributionItem(action);
350
		ci.setMode(ActionContributionItem.MODE_FORCE_TEXT);
351
		return ci;
352
	}
353
354
	private void setMarkColor(RGB color) {
355
		if (markColor != null) {
356
			markColor.dispose();
357
		}
358
		markColor = new Color(getShell().getDisplay(), color);
359
		if (workImageGC != null) {
360
			workImageGC.setForeground(markColor);
361
		}
362
363
		GC colorGC = new GC(colorIcon);
364
		colorGC.setBackground(markColor);
365
		colorGC.fillRectangle(0, 0, 16, 16);
366
		colorGC.drawRectangle(0, 0, 15, 15);
367
		colorGC.dispose();
368
369
		colorAction.setImageDescriptor(ImageDescriptor.createFromImage(colorIcon));
370
	}
371
253
	@Override
372
	@Override
254
	public void dispose() {
373
	public void dispose() {
374
		disposeImageResources();
375
		if (markColor != null) {
376
			markColor.dispose();
377
		}
378
		if (colorIcon != null) {
379
			colorIcon.dispose();
380
		}
381
255
		canvas.setCursor(null);
382
		canvas.setCursor(null);
256
		for (Cursor cursor : cursors.values()) {
383
		for (Cursor cursor : cursors.values()) {
257
			cursor.dispose();
384
			cursor.dispose();
Lines 259-266 Link Here
259
		super.dispose();
386
		super.dispose();
260
	}
387
	}
261
388
389
	private void disposeImageResources() {
390
		if (originalImage != null) {
391
			originalImage.dispose();
392
		}
393
		if (workImageGC != null) {
394
			workImageGC.dispose();
395
		}
396
		if (workImage != null) {
397
			workImage.dispose();
398
		}
399
	}
400
401
	private static final int CURSOR_MARK_TOOL = -1;
402
262
	private void allocateCursors() {
403
	private void allocateCursors() {
263
		Display display = Display.getCurrent();
404
		Display display = getShell().getDisplay();
264
		cursors.put(SWT.CURSOR_ARROW, new Cursor(display, SWT.CURSOR_ARROW));
405
		cursors.put(SWT.CURSOR_ARROW, new Cursor(display, SWT.CURSOR_ARROW));
265
		cursors.put(SWT.CURSOR_SIZEALL, new Cursor(display, SWT.CURSOR_SIZEALL));
406
		cursors.put(SWT.CURSOR_SIZEALL, new Cursor(display, SWT.CURSOR_SIZEALL));
266
		cursors.put(SWT.CURSOR_SIZENWSE, new Cursor(display, SWT.CURSOR_SIZENWSE));
407
		cursors.put(SWT.CURSOR_SIZENWSE, new Cursor(display, SWT.CURSOR_SIZENWSE));
Lines 268-273 Link Here
268
		cursors.put(SWT.CURSOR_SIZENS, new Cursor(display, SWT.CURSOR_SIZENS));
409
		cursors.put(SWT.CURSOR_SIZENS, new Cursor(display, SWT.CURSOR_SIZENS));
269
		cursors.put(SWT.CURSOR_SIZEWE, new Cursor(display, SWT.CURSOR_SIZEWE));
410
		cursors.put(SWT.CURSOR_SIZEWE, new Cursor(display, SWT.CURSOR_SIZEWE));
270
		cursors.put(SWT.CURSOR_CROSS, new Cursor(display, SWT.CURSOR_CROSS));
411
		cursors.put(SWT.CURSOR_CROSS, new Cursor(display, SWT.CURSOR_CROSS));
412
413
		// TODO: allocate custom cursor for "mark" tool
414
		cursors.put(CURSOR_MARK_TOOL, new Cursor(display, SWT.CURSOR_HAND));
271
	}
415
	}
272
416
273
	private Rectangle getScaledSelection() {
417
	private Rectangle getScaledSelection() {
Lines 278-302 Link Here
278
		int y = (int) Math.round(currentSelection.y * scaleFactor);
422
		int y = (int) Math.round(currentSelection.y * scaleFactor);
279
		int right = (int) Math.round((currentSelection.x + currentSelection.width) * scaleFactor);
423
		int right = (int) Math.round((currentSelection.x + currentSelection.width) * scaleFactor);
280
		int bottom = (int) Math.round((currentSelection.y + currentSelection.height) * scaleFactor);
424
		int bottom = (int) Math.round((currentSelection.y + currentSelection.height) * scaleFactor);
281
		int width = Math.min(right, (int) Math.round((screenshotImage.getBounds().width - 1) * scaleFactor)) - x;
425
		int width = Math.min(right, (int) Math.round((workImage.getBounds().width - 1) * scaleFactor)) - x;
282
		int height = Math.min(bottom, (int) Math.round((screenshotImage.getBounds().height - 1) * scaleFactor)) - y;
426
		int height = Math.min(bottom, (int) Math.round((workImage.getBounds().height - 1) * scaleFactor)) - y;
283
		return new Rectangle(x, y, width, height);
427
		return new Rectangle(x, y, width, height);
284
	}
428
	}
285
429
286
	@Override
430
	@Override
287
	public boolean isPageComplete() {
431
	public boolean isPageComplete() {
288
		if (screenshotImage == null)
432
		return workImage != null;
289
			return false;
290
		return true;
291
	}
433
	}
292
434
293
	@Override
435
	@Override
294
	public IWizardPage getNextPage() {
436
	public IWizardPage getNextPage() {
295
		NewAttachmentPage page = (NewAttachmentPage) getWizard().getPage("AttachmentDetails");
437
		NewAttachmentPage page = (NewAttachmentPage) getWizard().getPage("AttachmentDetails");
296
		attachment.setContentType("image/jpeg");
297
		page.setFilePath(InputAttachmentSourcePage.SCREENSHOT_LABEL);
438
		page.setFilePath(InputAttachmentSourcePage.SCREENSHOT_LABEL);
298
		page.setContentType();
439
		page.setContentType();
299
		getCropScreenshot();
300
		return page;
440
		return page;
301
	}
441
	}
302
442
Lines 306-314 Link Here
306
	}
446
	}
307
447
308
	private void captureScreenshotContent() {
448
	private void captureScreenshotContent() {
309
449
		Display display = getShell().getDisplay();
310
		final Display display = Display.getDefault();
450
		Shell wizardShell = getWizard().getContainer().getShell();
311
		final Shell wizardShell = getWizard().getContainer().getShell();
312
		wizardShell.setVisible(false);
451
		wizardShell.setVisible(false);
313
452
314
		// NOTE: need a wait since the shell can take time to disappear (e.g. fade on Vista)
453
		// NOTE: need a wait since the shell can take time to disappear (e.g. fade on Vista)
Lines 318-341 Link Here
318
			// ignore
457
			// ignore
319
		}
458
		}
320
459
321
		display.asyncExec(new Runnable() {
460
		disposeImageResources();
322
			public void run() {
461
		Rectangle displayBounds = display.getBounds();
323
				Rectangle displayBounds = display.getBounds();
462
		originalImage = new Image(display, displayBounds);
324
				screenshotImage = new Image(display, displayBounds);
463
		workImage = new Image(display, displayBounds);
464
465
		GC gc = new GC(display);
466
		gc.copyArea(originalImage, 0, 0);
467
		gc.copyArea(workImage, 0, 0);
468
		gc.dispose();
325
469
326
				GC gc = new GC(display);
470
		workImageGC = new GC(workImage);
327
				gc.copyArea(screenshotImage, 0, 0);
471
		workImageGC.setForeground(markColor);
328
				gc.dispose();
472
		workImageGC.setLineWidth(4);
473
		workImageGC.setLineCap(SWT.CAP_ROUND);
329
474
330
				clearSelection();
475
		clearSelection();
331
				refreshCanvasSize();
476
		refreshCanvasSize();
332
477
333
				wizardShell.setVisible(true);
478
		wizardShell.setVisible(true);
334
				if (screenshotImage != null) {
479
		setPageComplete(true);
335
					setPageComplete(true);
336
				}
337
			}
338
		});
339
	}
480
	}
340
481
341
	/**
482
	/**
Lines 352-358 Link Here
352
		currentSelection = new Rectangle(startX, startY, width, height);
493
		currentSelection = new Rectangle(startX, startY, width, height);
353
494
354
		// Decreases 1 pixel size from original image because Rectangle.intersect() consider them as right-bottom limit
495
		// Decreases 1 pixel size from original image because Rectangle.intersect() consider them as right-bottom limit
355
		Rectangle imageBounds = screenshotImage.getBounds();
496
		Rectangle imageBounds = workImage.getBounds();
356
		imageBounds.width--;
497
		imageBounds.width--;
357
		imageBounds.height--;
498
		imageBounds.height--;
358
		currentSelection.intersect(imageBounds);
499
		currentSelection.intersect(imageBounds);
Lines 363-374 Link Here
363
	 * level is changed
504
	 * level is changed
364
	 */
505
	 */
365
	private void setUpGrabPoints() {
506
	private void setUpGrabPoints() {
507
		grabPoints.clear();
366
		if (currentSelection == null) {
508
		if (currentSelection == null) {
367
			return;
509
			return;
368
		}
510
		}
369
511
370
		canvas.setCursor(null);
371
		grabPoints.clear();
372
		Rectangle scaledSelection = getScaledSelection();
512
		Rectangle scaledSelection = getScaledSelection();
373
		grabPoints.add(GrabPoint.createGrabPoint(scaledSelection.x, scaledSelection.y, SWT.CURSOR_SIZENWSE, EnumSet.of(
513
		grabPoints.add(GrabPoint.createGrabPoint(scaledSelection.x, scaledSelection.y, SWT.CURSOR_SIZENWSE, EnumSet.of(
374
				SelectionSide.LEFT, SelectionSide.TOP)));
514
				SelectionSide.LEFT, SelectionSide.TOP)));
Lines 393-399 Link Here
393
				originalSelection.height);
533
				originalSelection.height);
394
		int deltaX = x - startPoint.x;
534
		int deltaX = x - startPoint.x;
395
		int deltaY = y - startPoint.y;
535
		int deltaY = y - startPoint.y;
396
		Rectangle imageBounds = screenshotImage.getBounds();
536
		Rectangle imageBounds = workImage.getBounds();
397
537
398
		// Check current selection limits
538
		// Check current selection limits
399
		if (resizableSides.contains(SelectionSide.LEFT)) {
539
		if (resizableSides.contains(SelectionSide.LEFT)) {
Lines 436-441 Link Here
436
		if (resizableSides.contains(SelectionSide.BOTTOM)) {
576
		if (resizableSides.contains(SelectionSide.BOTTOM)) {
437
			currentSelection.height += deltaY;
577
			currentSelection.height += deltaY;
438
		}
578
		}
579
580
		setUpGrabPoints();
439
	}
581
	}
440
582
441
	private void refreshSelectionPosition(int x, int y) {
583
	private void refreshSelectionPosition(int x, int y) {
Lines 447-453 Link Here
447
		if (newY < 0) {
589
		if (newY < 0) {
448
			newY = 0;
590
			newY = 0;
449
		}
591
		}
450
		Rectangle imageBounds = screenshotImage.getBounds();
592
		Rectangle imageBounds = workImage.getBounds();
451
		if (newX + originalSelection.width - 1 > imageBounds.width) {
593
		if (newX + originalSelection.width - 1 > imageBounds.width) {
452
			newX = imageBounds.width - originalSelection.width;
594
			newX = imageBounds.width - originalSelection.width;
453
		}
595
		}
Lines 455-460 Link Here
455
			newY = imageBounds.height - originalSelection.height;
597
			newY = imageBounds.height - originalSelection.height;
456
		}
598
		}
457
		currentSelection = new Rectangle(newX, newY, originalSelection.width, originalSelection.height);
599
		currentSelection = new Rectangle(newX, newY, originalSelection.width, originalSelection.height);
600
601
		setUpGrabPoints();
458
	}
602
	}
459
603
460
	private void registerMouseListeners() {
604
	private void registerMouseListeners() {
Lines 464-480 Link Here
464
			 * If a selection is in course, moving the mouse around refreshes the selection rectangle
608
			 * If a selection is in course, moving the mouse around refreshes the selection rectangle
465
			 */
609
			 */
466
			public void mouseMove(MouseEvent e) {
610
			public void mouseMove(MouseEvent e) {
467
				if (currentAction == Action.SELECTING) {
611
				int scaledX = (int) Math.round(e.x / scaleFactor);
468
					// Selection in course
612
				int scaledY = (int) Math.round(e.y / scaleFactor);
469
					refreshCurrentSelection((int) Math.round(e.x / scaleFactor), (int) Math.round(e.y / scaleFactor));
613
614
				if (currentAction == EditorAction.SELECTING) {
615
					refreshCurrentSelection(scaledX, scaledY);
470
					canvas.redraw();
616
					canvas.redraw();
471
				} else if (currentAction == Action.RESIZING_SELECTION) {
617
				} else if (currentAction == EditorAction.RESIZING_SELECTION) {
472
					refreshSelectionResize((int) Math.round(e.x / scaleFactor), (int) Math.round(e.y / scaleFactor));
618
					refreshSelectionResize(scaledX, scaledY);
473
					canvas.redraw();
619
					canvas.redraw();
474
				} else if (currentAction == Action.MOVING_SELECTION) {
620
				} else if (currentAction == EditorAction.MOVING_SELECTION) {
475
					refreshSelectionPosition((int) Math.round(e.x / scaleFactor), (int) Math.round(e.y / scaleFactor));
621
					refreshSelectionPosition(scaledX, scaledY);
476
					canvas.redraw();
622
					canvas.redraw();
477
				} else if (currentAction == Action.IDLE && currentSelection != null) {
623
				} else if (currentAction == EditorAction.CROPPING && currentSelection != null) {
478
					boolean cursorSet = false;
624
					boolean cursorSet = false;
479
625
480
					// No selection in course, but have something selected; first test if I'm hovering some grab point
626
					// No selection in course, but have something selected; first test if I'm hovering some grab point
Lines 491-498 Link Here
491
						canvas.setCursor(cursors.get(SWT.CURSOR_SIZEALL));
637
						canvas.setCursor(cursors.get(SWT.CURSOR_SIZEALL));
492
						cursorSet = true;
638
						cursorSet = true;
493
					}
639
					}
494
					if (!cursorSet && canvas.getCursor() != null) {
640
495
						canvas.setCursor(null);
641
					// If I'm out, the default cursor for cropping mode is cross
642
					Cursor crossCursor = cursors.get(SWT.CURSOR_CROSS);
643
					if (!cursorSet && canvas.getCursor() != crossCursor) {
644
						canvas.setCursor(crossCursor);
645
					}
646
				} else if (currentAction == EditorAction.MARKING) {
647
					drawMarkLine(scaledX, scaledY);
648
649
					Cursor markCursor = cursors.get(CURSOR_MARK_TOOL);
650
					if (canvas.getCursor() != markCursor) {
651
						canvas.setCursor(markCursor);
496
					}
652
					}
497
				}
653
				}
498
			}
654
			}
Lines 502-522 Link Here
502
		canvas.addMouseListener(new MouseAdapter() {
658
		canvas.addMouseListener(new MouseAdapter() {
503
659
504
			/**
660
			/**
505
			 * Releasing the mouse button ends the selection; compute the selection rectangle and redraw the cropped
661
			 * Releasing the mouse button ends the selection or a drawing; compute the selection rectangle and redraw
506
			 * image
662
			 * the cropped image
507
			 */
663
			 */
508
			public void mouseUp(MouseEvent e) {
664
			public void mouseUp(MouseEvent e) {
509
				if (currentAction == Action.SELECTING || currentAction == Action.RESIZING_SELECTION
665
				if (currentAction == EditorAction.SELECTING || currentAction == EditorAction.RESIZING_SELECTION
510
						|| currentAction == Action.MOVING_SELECTION) {
666
						|| currentAction == EditorAction.MOVING_SELECTION) {
511
					canvas.setCursor(cursors.get(SWT.CURSOR_ARROW));
512
513
					int scaledX = (int) Math.round(e.x / scaleFactor);
667
					int scaledX = (int) Math.round(e.x / scaleFactor);
514
					int scaledY = (int) Math.round(e.y / scaleFactor);
668
					int scaledY = (int) Math.round(e.y / scaleFactor);
515
					if (currentAction == Action.SELECTING) {
669
					if (currentAction == EditorAction.SELECTING) {
516
						refreshCurrentSelection(scaledX, scaledY);
670
						refreshCurrentSelection(scaledX, scaledY);
517
					} else if (currentAction == Action.RESIZING_SELECTION) {
671
					} else if (currentAction == EditorAction.RESIZING_SELECTION) {
518
						refreshSelectionResize(scaledX, scaledY);
672
						refreshSelectionResize(scaledX, scaledY);
519
					} else if (currentAction == Action.MOVING_SELECTION) {
673
					} else if (currentAction == EditorAction.MOVING_SELECTION) {
520
						refreshSelectionPosition(scaledX, scaledY);
674
						refreshSelectionPosition(scaledX, scaledY);
521
					}
675
					}
522
					if (currentSelection.width == 0 && currentSelection.height == 0) {
676
					if (currentSelection.width == 0 && currentSelection.height == 0) {
Lines 524-540 Link Here
524
					}
678
					}
525
					setUpGrabPoints();
679
					setUpGrabPoints();
526
					startPoint = null;
680
					startPoint = null;
527
					currentAction = Action.IDLE;
681
					currentAction = EditorAction.CROPPING;
528
682
529
					canvas.redraw();
683
					canvas.redraw();
684
					markAttachmentDirty();
685
				} else if (currentAction == EditorAction.MARKING) {
686
					startPoint = null;
687
					markAttachmentDirty();
530
				}
688
				}
531
			}
689
			}
532
690
533
			/**
691
			/**
534
			 * Pressing mouse button starts a selection; normalizes and marks the start point
692
			 * Pressing mouse button starts a selection or a drawing; normalizes and marks the start point
535
			 */
693
			 */
536
			public void mouseDown(MouseEvent e) {
694
			public void mouseDown(MouseEvent e) {
537
				if (currentAction != Action.IDLE) {
695
				int scaledX = (int) (e.x / scaleFactor);
696
				int scaledY = (int) (e.y / scaleFactor);
697
698
				if (currentAction == EditorAction.MARKING) {
699
					startPoint = new Point(scaledX, scaledY);
700
					drawMarkLine(scaledX, scaledY);
701
					canvas.setCursor(cursors.get(CURSOR_MARK_TOOL));
702
					return;
703
				} else if (currentAction != EditorAction.CROPPING) {
538
					return;
704
					return;
539
				}
705
				}
540
706
Lines 543-551 Link Here
543
					for (GrabPoint point : grabPoints) {
709
					for (GrabPoint point : grabPoints) {
544
						if (point.grabArea.contains(e.x, e.y)) {
710
						if (point.grabArea.contains(e.x, e.y)) {
545
							originalSelection = currentSelection;
711
							originalSelection = currentSelection;
546
							currentAction = Action.RESIZING_SELECTION;
712
							currentAction = EditorAction.RESIZING_SELECTION;
547
							resizableSides = point.resizableSides;
713
							resizableSides = point.resizableSides;
548
							startPoint = new Point((int) (e.x / scaleFactor), (int) (e.y / scaleFactor));
714
							startPoint = new Point(scaledX, scaledY);
549
							canvas.redraw();
715
							canvas.redraw();
550
							return;
716
							return;
551
						}
717
						}
Lines 553-572 Link Here
553
				}
719
				}
554
720
555
				// Check if I could move the selection
721
				// Check if I could move the selection
556
				if (currentSelection != null
722
				if (currentSelection != null && currentSelection.contains(scaledX, scaledY)) {
557
						&& currentSelection.contains((int) (e.x / scaleFactor), (int) (e.y / scaleFactor))) {
558
					originalSelection = currentSelection;
723
					originalSelection = currentSelection;
559
					currentAction = Action.MOVING_SELECTION;
724
					currentAction = EditorAction.MOVING_SELECTION;
560
					startPoint = new Point((int) (e.x / scaleFactor), (int) (e.y / scaleFactor));
725
					startPoint = new Point(scaledX, scaledY);
561
					canvas.redraw();
726
					canvas.redraw();
562
					return;
727
					return;
563
				}
728
				}
564
729
565
				// Do a simple selection
730
				// Do a simple selection
566
				canvas.setCursor(cursors.get(SWT.CURSOR_CROSS));
731
				canvas.setCursor(cursors.get(SWT.CURSOR_CROSS));
567
				currentAction = Action.SELECTING;
732
				currentAction = EditorAction.SELECTING;
568
				currentSelection = null;
733
				currentSelection = null;
569
				startPoint = new Point((int) (e.x / scaleFactor), (int) (e.y / scaleFactor));
734
				startPoint = new Point(scaledX, scaledY);
735
				setUpGrabPoints();
570
				canvas.redraw();
736
				canvas.redraw();
571
			}
737
			}
572
738
Lines 577-589 Link Here
577
	private void clearSelection() {
743
	private void clearSelection() {
578
		currentSelection = null;
744
		currentSelection = null;
579
		startPoint = null;
745
		startPoint = null;
746
		markAttachmentDirty();
580
	}
747
	}
581
748
582
	/**
749
	/**
583
	 * Recalculates image canvas size based on "fit on canvas" setting, set up the grab points, and redraws
750
	 * Recalculates image canvas size based on "fit on canvas" setting, set up the grab points, and redraws
584
	 * <p>
751
	 * <p>
585
	 * This method should be called whenever the {@link #screenshotImage image} <strong>visible</strong> size is
752
	 * This method should be called whenever the {@link #workImage image} <strong>visible</strong> size is changed,
586
	 * changed, which can happen when:
753
	 * which can happen when:
587
	 * <p>
754
	 * <p>
588
	 * <ul>
755
	 * <ul>
589
	 * <li>The "Fit Image" setting is changed, so the image zoom level changes
756
	 * <li>The "Fit Image" setting is changed, so the image zoom level changes
Lines 595-609 Link Here
595
	 * Calling this method under other circumstances may lead to strange behavior in the scrolled composite
762
	 * Calling this method under other circumstances may lead to strange behavior in the scrolled composite
596
	 */
763
	 */
597
	private void refreshCanvasSize() {
764
	private void refreshCanvasSize() {
598
		if (fitButton.getSelection()) {
765
		if (fitAction.isChecked()) {
599
			// This little hack is necessary to get the client area without scrollbars; 
766
			// This little hack is necessary to get the client area without scrollbars; 
600
			// they'll be automatically restored if necessary after Canvas.setBounds()
767
			// they'll be automatically restored if necessary after Canvas.setBounds()
601
			scrolledComposite.getHorizontalBar().setVisible(false);
768
			scrolledComposite.getHorizontalBar().setVisible(false);
602
			scrolledComposite.getVerticalBar().setVisible(false);
769
			scrolledComposite.getVerticalBar().setVisible(false);
603
770
604
			Rectangle bounds = scrolledComposite.getClientArea();
771
			Rectangle bounds = scrolledComposite.getClientArea();
605
			if (screenshotImage != null) {
772
			if (workImage != null) {
606
				Rectangle imageBounds = screenshotImage.getBounds();
773
				Rectangle imageBounds = workImage.getBounds();
607
				if (imageBounds.width > bounds.width || imageBounds.height > bounds.height) {
774
				if (imageBounds.width > bounds.width || imageBounds.height > bounds.height) {
608
					double xRatio = (double) bounds.width / imageBounds.width;
775
					double xRatio = (double) bounds.width / imageBounds.width;
609
					double yRatio = (double) bounds.height / imageBounds.height;
776
					double yRatio = (double) bounds.height / imageBounds.height;
Lines 616-623 Link Here
616
		} else {
783
		} else {
617
			scaleFactor = 1.0;
784
			scaleFactor = 1.0;
618
			Rectangle bounds = scrolledComposite.getClientArea();
785
			Rectangle bounds = scrolledComposite.getClientArea();
619
			if (screenshotImage != null) {
786
			if (workImage != null) {
620
				Rectangle imageBounds = screenshotImage.getBounds();
787
				Rectangle imageBounds = workImage.getBounds();
621
				bounds.width = imageBounds.width;
788
				bounds.width = imageBounds.width;
622
				bounds.height = imageBounds.height;
789
				bounds.height = imageBounds.height;
623
			}
790
			}
Lines 628-652 Link Here
628
	}
795
	}
629
796
630
	/**
797
	/**
631
	 * Decorates the screenshot canvas with the selection rectangle; this is done while still selecting, so it does not
632
	 * have all adornments of a {@link #drawSelection(GC) finished} selection
633
	 */
634
	@SuppressWarnings("deprecation")
635
	private void drawSelectionPreview(GC gc) {
636
		gc.setLineDash(new int[] { 4 });
637
		gc.setXORMode(true);
638
		gc.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_WHITE));
639
		gc.drawRectangle(getScaledSelection());
640
		gc.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
641
		gc.drawRectangle(getScaledSelection());
642
		gc.setXORMode(false);
643
		gc.setLineStyle(SWT.LINE_SOLID);
644
	}
645
646
	/**
647
	 * Decorates the screenshot canvas with the selection rectangle, resize grab points and other adornments
798
	 * Decorates the screenshot canvas with the selection rectangle, resize grab points and other adornments
648
	 */
799
	 */
649
	private void drawSelection(GC gc) {
800
	private void drawSelection(GC gc) {
801
		if (currentSelection == null) {
802
			return;
803
		}
650
		Rectangle scaledSelection = getScaledSelection();
804
		Rectangle scaledSelection = getScaledSelection();
651
805
652
		// Draw shadow
806
		// Draw shadow
Lines 666-683 Link Here
666
820
667
		// Draw selection rectangle
821
		// Draw selection rectangle
668
		gc.setLineStyle(SWT.LINE_SOLID);
822
		gc.setLineStyle(SWT.LINE_SOLID);
669
		gc.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_DARK_GRAY));
823
		gc.setForeground(getShell().getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY));
670
		gc.drawRectangle(scaledSelection);
824
		gc.drawRectangle(scaledSelection);
671
825
672
		// Draw grab points
826
		// Draw grab points
673
		gc.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE));
827
		gc.setBackground(getShell().getDisplay().getSystemColor(SWT.COLOR_WHITE));
674
		gc.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_BLACK));
828
		gc.setForeground(getShell().getDisplay().getSystemColor(SWT.COLOR_BLACK));
675
		for (GrabPoint point : grabPoints) {
829
		for (GrabPoint point : grabPoints) {
676
			gc.fillRectangle(point.grabArea);
830
			gc.fillRectangle(point.grabArea);
677
			gc.drawRectangle(point.grabArea);
831
			gc.drawRectangle(point.grabArea);
678
		}
832
		}
679
	}
833
	}
680
834
835
	/**
836
	 * Connects the previous mark point to the new reference point, by drawing a new line
837
	 */
838
	private void drawMarkLine(int x, int y) {
839
		if (startPoint != null) {
840
			clearAction.setEnabled(true);
841
			workImageGC.drawLine(startPoint.x, startPoint.y, x, y);
842
			startPoint.x = x;
843
			startPoint.y = y;
844
			canvas.redraw();
845
		}
846
	}
847
681
	private static enum SelectionSide {
848
	private static enum SelectionSide {
682
849
683
		LEFT, RIGHT, TOP, BOTTOM;
850
		LEFT, RIGHT, TOP, BOTTOM;
Lines 706-727 Link Here
706
873
707
	private List<GrabPoint> grabPoints = new ArrayList<GrabPoint>(8);
874
	private List<GrabPoint> grabPoints = new ArrayList<GrabPoint>(8);
708
875
709
	private Image getCropScreenshot() {
876
	/**
710
		if (currentSelection == null) {
877
	 * Creates the final screenshot
711
			return screenshotImage;
878
	 * 
879
	 * @return The final screenshot, with all markings, and cropped according to user settings; <strong>The caller is
880
	 *         responsible for disposing the returned image</strong>
881
	 */
882
	public Image createImage() {
883
		Image screenshot = new Image(getShell().getDisplay(), currentSelection != null ? currentSelection
884
				: workImage.getBounds());
885
886
		GC gc = new GC(screenshot);
887
		if (currentSelection != null) {
888
			gc.drawImage(workImage, currentSelection.x, currentSelection.y, currentSelection.width,
889
					currentSelection.height, 0, 0, currentSelection.width, currentSelection.height);
890
		} else {
891
			gc.drawImage(workImage, 0, 0);
712
		}
892
		}
713
714
		Image image = new Image(Display.getDefault(), currentSelection);
715
		GC gc = new GC(image);
716
		gc.drawImage(screenshotImage, currentSelection.x, currentSelection.y, currentSelection.width,
717
				currentSelection.height, 0, 0, currentSelection.width, currentSelection.height);
718
		gc.dispose();
893
		gc.dispose();
719
894
720
		return image;
895
		return screenshot;
721
	}
896
	}
722
897
723
	public Image getScreenshotImage() {
898
	private void markAttachmentDirty() {
724
		return getCropScreenshot();
899
		NewAttachmentWizard wizard = (NewAttachmentWizard) getWizard();
900
		((ImageAttachment) wizard.getAttachment()).markDirty();
725
	}
901
	}
726
902
727
}
903
}

Return to bug 195691