### Eclipse Workspace Patch 1.0 #P org.eclipse.mylyn.tasks.ui Index: src/org/eclipse/mylyn/internal/tasks/ui/wizards/ScreenshotAttachmentPage.java =================================================================== RCS file: /cvsroot/tools/org.eclipse.mylyn/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/internal/tasks/ui/wizards/ScreenshotAttachmentPage.java,v retrieving revision 1.4 diff -u -r1.4 ScreenshotAttachmentPage.java --- src/org/eclipse/mylyn/internal/tasks/ui/wizards/ScreenshotAttachmentPage.java 16 Nov 2007 06:24:49 -0000 1.4 +++ src/org/eclipse/mylyn/internal/tasks/ui/wizards/ScreenshotAttachmentPage.java 17 Nov 2007 20:40:56 -0000 @@ -32,15 +32,18 @@ import org.eclipse.swt.events.PaintListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Cursor; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.graphics.Region; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Canvas; +import org.eclipse.swt.widgets.ColorDialog; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; @@ -69,7 +72,32 @@ private Button fitButton; - private Image screenshotImage; + private Button cropButton; + + private Button markButton; + + private Button colorButton; + + private Image colorIcon; + + private Color markColor; + + private Button resetButton; + + /** + * Original screenshot image; used for backup purposes + */ + private Image originalImage; + + /** + * Copy of {@link #originalImage original} image; all drawing operations are done here; base for the result image + */ + private Image workImage; + + /** + * Used to draw into {@link #workImage} + */ + private GC workImageGC; private Canvas canvas; @@ -87,8 +115,9 @@ private Rectangle originalSelection; /** - * Temporary storage for selection start point or selection resizing initial reference point; this value is - * normalized to real image coordinates, no matter the zoom level (see {@link #scaleFactor}) + * Temporary storage for selection start point, selection resizing initial reference point or previous mark point + * (it depends on current tool); this value is normalized to real image coordinates, no matter the zoom level (see + * {@link #scaleFactor}) */ private Point startPoint; @@ -112,7 +141,7 @@ */ private static enum Action { - IDLE, SELECTING, RESIZING_SELECTION, MOVING_SELECTION; + IDLE, SELECTING, RESIZING_SELECTION, MOVING_SELECTION, MARKING; }; @@ -131,7 +160,6 @@ } public void createControl(Composite parent) { - Composite composite = new Composite(parent, SWT.NONE); composite.setLayout(new GridLayout()); setControl(composite); @@ -139,16 +167,25 @@ allocateCursors(); Composite buttonsComposite = new Composite(composite, SWT.NONE); - buttonsComposite.setLayout(new GridLayout(3, false)); + buttonsComposite.setLayout(new GridLayout(6, false)); captureButton = new Button(buttonsComposite, SWT.PUSH); - captureButton.setText("Capture Desktop"); + captureButton.setText("&Capture Desktop"); captureButton.setImage(TasksUiImages.getImage(TasksUiImages.IMAGE_CAPTURE)); captureButton.addSelectionListener(new SelectionAdapter() { + private boolean isFirstCapture = true; + public void widgetSelected(SelectionEvent e) { captureScreenshotContent(); page.setErrorMessage(null); - fitButton.setEnabled(true); + if (isFirstCapture) { + isFirstCapture = false; + fitButton.setEnabled(true); + cropButton.setEnabled(true); + cropButton.setSelection(true); + markButton.setEnabled(true); + resetButton.setEnabled(true); + } } }); @@ -187,7 +224,7 @@ // }); fitButton = new Button(buttonsComposite, SWT.TOGGLE); - fitButton.setText("Fit Image"); + fitButton.setText("&Fit Image"); fitButton.setImage(TasksUiImages.getImage(TasksUiImages.IMAGE_FIT)); fitButton.addSelectionListener(new SelectionAdapter() { @@ -199,6 +236,69 @@ fitButton.setSelection(true); fitButton.setEnabled(false); + cropButton = new Button(buttonsComposite, SWT.TOGGLE); + cropButton.setText("Cro&p"); + cropButton.addSelectionListener(new SelectionAdapter() { + + @Override + public void widgetSelected(SelectionEvent e) { + currentAction = Action.IDLE; + cropButton.setSelection(true); + markButton.setSelection(false); + colorButton.setEnabled(false); + canvas.redraw(); + } + + }); + cropButton.setEnabled(false); + + markButton = new Button(buttonsComposite, SWT.TOGGLE); + markButton.setText("&Mark"); + markButton.addSelectionListener(new SelectionAdapter() { + + @Override + public void widgetSelected(SelectionEvent e) { + currentAction = Action.MARKING; + cropButton.setSelection(false); + markButton.setSelection(true); + colorButton.setEnabled(true); + canvas.redraw(); + } + + }); + markButton.setEnabled(false); + + colorButton = new Button(buttonsComposite, SWT.PUSH); + colorButton.setText("Change Co&lor..."); + colorIcon = new Image(Display.getCurrent(), 16, 16); + setMarkColor(new RGB(255, 0, 0)); + colorButton.setEnabled(false); + colorButton.addSelectionListener(new SelectionAdapter() { + + @Override + public void widgetSelected(SelectionEvent e) { + ColorDialog dialog = new ColorDialog(getControl().getShell()); + RGB color = dialog.open(); + if (color != null) { + setMarkColor(color); + } + } + + }); + + resetButton = new Button(buttonsComposite, SWT.PUSH); + resetButton.setText("&Reset Image"); + resetButton.addSelectionListener(new SelectionAdapter() { + + @Override + public void widgetSelected(SelectionEvent e) { + workImageGC.drawImage(originalImage, 0, 0); + canvas.redraw(); + } + + }); + resetButton.setEnabled(false); + scrolledComposite = new ScrolledComposite(composite, SWT.V_SCROLL | SWT.H_SCROLL | SWT.BORDER); scrolledComposite.setLayoutData(GridDataFactory.fillDefaults() .align(SWT.FILL, SWT.FILL) @@ -210,15 +310,15 @@ canvas.addPaintListener(new PaintListener() { public void paintControl(PaintEvent e) { - if (screenshotImage != null) { - Rectangle imageBounds = screenshotImage.getBounds(); + if (workImage != null) { + Rectangle imageBounds = workImage.getBounds(); Rectangle canvasBounds = canvas.getClientArea(); if (fitButton.getSelection()) { - e.gc.drawImage(screenshotImage, 0, 0, imageBounds.width, imageBounds.height, 0, 0, + e.gc.drawImage(workImage, 0, 0, imageBounds.width, imageBounds.height, 0, 0, canvasBounds.width, canvasBounds.height); } else { - e.gc.drawImage(screenshotImage, 0, 0); + e.gc.drawImage(workImage, 0, 0); } if (currentAction == Action.IDLE) { @@ -226,7 +326,7 @@ drawSelection(e.gc); } } else if (currentAction == Action.SELECTING || currentAction == Action.RESIZING_SELECTION - || currentAction == Action.MOVING_SELECTION) { + || currentAction == Action.MOVING_SELECTION || currentAction == Action.MARKING) { if (currentSelection != null) { drawSelectionPreview(e.gc); } @@ -250,8 +350,34 @@ registerMouseListeners(); } + private void setMarkColor(RGB color) { + if (markColor != null) { + markColor.dispose(); + } + markColor = new Color(Display.getCurrent(), color); + if (workImageGC != null) { + workImageGC.setForeground(markColor); + } + + GC colorGC = new GC(colorIcon); + colorGC.setBackground(markColor); + colorGC.fillRectangle(0, 0, 15, 15); + colorGC.drawRectangle(0, 0, 15, 15); + colorGC.dispose(); + + colorButton.setImage(colorIcon); + } + @Override public void dispose() { + disposeImageResources(); + if (markColor != null) { + markColor.dispose(); + } + if (colorIcon != null) { + colorIcon.dispose(); + } + canvas.setCursor(null); for (Cursor cursor : cursors.values()) { cursor.dispose(); @@ -259,6 +385,20 @@ super.dispose(); } + private void disposeImageResources() { + if (originalImage != null) { + originalImage.dispose(); + } + if (workImageGC != null) { + workImageGC.dispose(); + } + if (workImage != null) { + workImage.dispose(); + } + } + + private static final int CURSOR_MARK_TOOL = -1; + private void allocateCursors() { Display display = Display.getCurrent(); cursors.put(SWT.CURSOR_ARROW, new Cursor(display, SWT.CURSOR_ARROW)); @@ -268,6 +408,9 @@ cursors.put(SWT.CURSOR_SIZENS, new Cursor(display, SWT.CURSOR_SIZENS)); cursors.put(SWT.CURSOR_SIZEWE, new Cursor(display, SWT.CURSOR_SIZEWE)); cursors.put(SWT.CURSOR_CROSS, new Cursor(display, SWT.CURSOR_CROSS)); + + // TODO: allocate custom cursor for "mark" tool + cursors.put(CURSOR_MARK_TOOL, new Cursor(display, SWT.CURSOR_HAND)); } private Rectangle getScaledSelection() { @@ -278,16 +421,14 @@ int y = (int) Math.round(currentSelection.y * scaleFactor); int right = (int) Math.round((currentSelection.x + currentSelection.width) * scaleFactor); int bottom = (int) Math.round((currentSelection.y + currentSelection.height) * scaleFactor); - int width = Math.min(right, (int) Math.round((screenshotImage.getBounds().width - 1) * scaleFactor)) - x; - int height = Math.min(bottom, (int) Math.round((screenshotImage.getBounds().height - 1) * scaleFactor)) - y; + int width = Math.min(right, (int) Math.round((workImage.getBounds().width - 1) * scaleFactor)) - x; + int height = Math.min(bottom, (int) Math.round((workImage.getBounds().height - 1) * scaleFactor)) - y; return new Rectangle(x, y, width, height); } @Override public boolean isPageComplete() { - if (screenshotImage == null) - return false; - return true; + return workImage != null; } @Override @@ -296,7 +437,6 @@ attachment.setContentType("image/jpeg"); page.setFilePath(InputAttachmentSourcePage.SCREENSHOT_LABEL); page.setContentType(); - getCropScreenshot(); return page; } @@ -306,9 +446,8 @@ } private void captureScreenshotContent() { - - final Display display = Display.getDefault(); - final Shell wizardShell = getWizard().getContainer().getShell(); + Display display = Display.getDefault(); + Shell wizardShell = getWizard().getContainer().getShell(); wizardShell.setVisible(false); // NOTE: need a wait since the shell can take time to disappear (e.g. fade on Vista) @@ -318,24 +457,26 @@ // ignore } - display.asyncExec(new Runnable() { - public void run() { - Rectangle displayBounds = display.getBounds(); - screenshotImage = new Image(display, displayBounds); - - GC gc = new GC(display); - gc.copyArea(screenshotImage, 0, 0); - gc.dispose(); + disposeImageResources(); + Rectangle displayBounds = display.getBounds(); + originalImage = new Image(display, displayBounds); + workImage = new Image(display, displayBounds); + + GC gc = new GC(display); + gc.copyArea(originalImage, 0, 0); + gc.copyArea(workImage, 0, 0); + gc.dispose(); - clearSelection(); - refreshCanvasSize(); + workImageGC = new GC(workImage); + workImageGC.setForeground(markColor); + workImageGC.setLineWidth(4); + workImageGC.setLineCap(SWT.CAP_ROUND); - wizardShell.setVisible(true); - if (screenshotImage != null) { - setPageComplete(true); - } - } - }); + clearSelection(); + refreshCanvasSize(); + + wizardShell.setVisible(true); + setPageComplete(true); } /** @@ -352,7 +493,7 @@ currentSelection = new Rectangle(startX, startY, width, height); // Decreases 1 pixel size from original image because Rectangle.intersect() consider them as right-bottom limit - Rectangle imageBounds = screenshotImage.getBounds(); + Rectangle imageBounds = workImage.getBounds(); imageBounds.width--; imageBounds.height--; currentSelection.intersect(imageBounds); @@ -367,7 +508,6 @@ return; } - canvas.setCursor(null); grabPoints.clear(); Rectangle scaledSelection = getScaledSelection(); grabPoints.add(GrabPoint.createGrabPoint(scaledSelection.x, scaledSelection.y, SWT.CURSOR_SIZENWSE, EnumSet.of( @@ -393,7 +533,7 @@ originalSelection.height); int deltaX = x - startPoint.x; int deltaY = y - startPoint.y; - Rectangle imageBounds = screenshotImage.getBounds(); + Rectangle imageBounds = workImage.getBounds(); // Check current selection limits if (resizableSides.contains(SelectionSide.LEFT)) { @@ -447,7 +587,7 @@ if (newY < 0) { newY = 0; } - Rectangle imageBounds = screenshotImage.getBounds(); + Rectangle imageBounds = workImage.getBounds(); if (newX + originalSelection.width - 1 > imageBounds.width) { newX = imageBounds.width - originalSelection.width; } @@ -464,15 +604,17 @@ * If a selection is in course, moving the mouse around refreshes the selection rectangle */ public void mouseMove(MouseEvent e) { + int scaledX = (int) Math.round(e.x / scaleFactor); + int scaledY = (int) Math.round(e.y / scaleFactor); + if (currentAction == Action.SELECTING) { - // Selection in course - refreshCurrentSelection((int) Math.round(e.x / scaleFactor), (int) Math.round(e.y / scaleFactor)); + refreshCurrentSelection(scaledX, scaledY); canvas.redraw(); } else if (currentAction == Action.RESIZING_SELECTION) { - refreshSelectionResize((int) Math.round(e.x / scaleFactor), (int) Math.round(e.y / scaleFactor)); + refreshSelectionResize(scaledX, scaledY); canvas.redraw(); } else if (currentAction == Action.MOVING_SELECTION) { - refreshSelectionPosition((int) Math.round(e.x / scaleFactor), (int) Math.round(e.y / scaleFactor)); + refreshSelectionPosition(scaledX, scaledY); canvas.redraw(); } else if (currentAction == Action.IDLE && currentSelection != null) { boolean cursorSet = false; @@ -491,8 +633,18 @@ canvas.setCursor(cursors.get(SWT.CURSOR_SIZEALL)); cursorSet = true; } - if (!cursorSet && canvas.getCursor() != null) { - canvas.setCursor(null); + + // If I'm out, the default cursor for cropping mode is cross + Cursor crossCursor = cursors.get(SWT.CURSOR_CROSS); + if (!cursorSet && canvas.getCursor() != crossCursor) { + canvas.setCursor(crossCursor); + } + } else if (currentAction == Action.MARKING) { + drawMarkLine(scaledX, scaledY); + + Cursor markCursor = cursors.get(CURSOR_MARK_TOOL); + if (canvas.getCursor() != markCursor) { + canvas.setCursor(markCursor); } } } @@ -508,8 +660,6 @@ public void mouseUp(MouseEvent e) { if (currentAction == Action.SELECTING || currentAction == Action.RESIZING_SELECTION || currentAction == Action.MOVING_SELECTION) { - canvas.setCursor(cursors.get(SWT.CURSOR_ARROW)); - int scaledX = (int) Math.round(e.x / scaleFactor); int scaledY = (int) Math.round(e.y / scaleFactor); if (currentAction == Action.SELECTING) { @@ -527,6 +677,8 @@ currentAction = Action.IDLE; canvas.redraw(); + } else if (currentAction == Action.MARKING) { + startPoint = null; } } @@ -534,7 +686,15 @@ * Pressing mouse button starts a selection; normalizes and marks the start point */ public void mouseDown(MouseEvent e) { - if (currentAction != Action.IDLE) { + int scaledX = (int) (e.x / scaleFactor); + int scaledY = (int) (e.y / scaleFactor); + + if (currentAction == Action.MARKING) { + startPoint = new Point(scaledX, scaledY); + drawMarkLine(scaledX, scaledY); + canvas.setCursor(cursors.get(CURSOR_MARK_TOOL)); + return; + } else if (currentAction != Action.IDLE) { return; } @@ -545,7 +705,7 @@ originalSelection = currentSelection; currentAction = Action.RESIZING_SELECTION; resizableSides = point.resizableSides; - startPoint = new Point((int) (e.x / scaleFactor), (int) (e.y / scaleFactor)); + startPoint = new Point(scaledX, scaledY); canvas.redraw(); return; } @@ -553,11 +713,10 @@ } // Check if I could move the selection - if (currentSelection != null - && currentSelection.contains((int) (e.x / scaleFactor), (int) (e.y / scaleFactor))) { + if (currentSelection != null && currentSelection.contains(scaledX, scaledY)) { originalSelection = currentSelection; currentAction = Action.MOVING_SELECTION; - startPoint = new Point((int) (e.x / scaleFactor), (int) (e.y / scaleFactor)); + startPoint = new Point(scaledX, scaledY); canvas.redraw(); return; } @@ -566,7 +725,7 @@ canvas.setCursor(cursors.get(SWT.CURSOR_CROSS)); currentAction = Action.SELECTING; currentSelection = null; - startPoint = new Point((int) (e.x / scaleFactor), (int) (e.y / scaleFactor)); + startPoint = new Point(scaledX, scaledY); canvas.redraw(); } @@ -582,8 +741,8 @@ /** * Recalculates image canvas size based on "fit on canvas" setting, set up the grab points, and redraws *

- * This method should be called whenever the {@link #screenshotImage image} visible size is - * changed, which can happen when: + * This method should be called whenever the {@link #workImage image} visible size is changed, + * which can happen when: *

*