001: /*
002: * uDig - User Friendly Desktop Internet GIS client http://udig.refractions.net (C) 2004, Refractions Research Inc. This
003: * library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
004: * License as published by the Free Software Foundation; version 2.1 of the License. This library is distributed in the
005: * hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
006: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
007: */
008: package net.refractions.udig.project.ui.internal.render.displayAdapter.impl;
009:
010: import java.awt.Cursor;
011: import java.awt.Dimension;
012: import java.awt.image.BufferedImage;
013: import java.awt.image.RenderedImage;
014:
015: import net.refractions.udig.internal.ui.UiPlugin;
016: import net.refractions.udig.project.internal.ProjectPlugin;
017: import net.refractions.udig.project.internal.render.RenderManager;
018: import net.refractions.udig.project.preferences.PreferenceConstants;
019: import net.refractions.udig.project.render.displayAdapter.IMapDisplayListener;
020: import net.refractions.udig.project.ui.commands.IDrawCommand;
021: import net.refractions.udig.project.ui.internal.MapEditor;
022: import net.refractions.udig.project.ui.internal.ProjectUIPlugin;
023: import net.refractions.udig.project.ui.internal.Trace;
024: import net.refractions.udig.project.ui.render.displayAdapter.MapMouseListener;
025: import net.refractions.udig.project.ui.render.displayAdapter.MapMouseMotionListener;
026: import net.refractions.udig.project.ui.render.displayAdapter.MapMouseWheelListener;
027: import net.refractions.udig.project.ui.render.displayAdapter.ViewportPane;
028: import net.refractions.udig.ui.graphics.NonAdvancedSWTGraphics;
029: import net.refractions.udig.ui.graphics.SWTGraphics;
030: import net.refractions.udig.ui.graphics.ViewportGraphics;
031:
032: import org.eclipse.core.runtime.Platform;
033: import org.eclipse.jface.preference.IPreferenceStore;
034: import org.eclipse.swt.SWT;
035: import org.eclipse.swt.events.PaintEvent;
036: import org.eclipse.swt.events.PaintListener;
037: import org.eclipse.swt.graphics.GC;
038: import org.eclipse.swt.graphics.Image;
039: import org.eclipse.swt.graphics.Point;
040: import org.eclipse.swt.graphics.Rectangle;
041: import org.eclipse.swt.widgets.Canvas;
042: import org.eclipse.swt.widgets.Composite;
043: import org.eclipse.swt.widgets.Control;
044: import org.eclipse.swt.widgets.Display;
045: import org.eclipse.swt.widgets.Event;
046: import org.eclipse.swt.widgets.Listener;
047: import org.eclipse.ui.PlatformUI;
048:
049: /**
050: * The ViewportPaneImpl is a java.awt.Panel that is the display area for a Map. It Registers itself
051: * with a RenderStack and obtains the image from the RenderStack if the RenderStack is "ready"
052: *
053: * @author Jesse Eichar
054: * @version $Revision: 1.9 $
055: */
056: public class ViewportPaneSWT extends Canvas implements ViewportPane {
057: private final Repainter REPAINT = new Repainter();
058:
059: private static final long serialVersionUID = 1L;
060:
061: private ViewportPainter painter = new ViewportPainter(this );
062:
063: EventJob eventJob = new EventJob();
064:
065: private RenderManager renderManager;
066:
067: private EventHandler handler;
068:
069: private MapEditor editor;
070:
071: Dimension displaySize = new Dimension(0, 0);
072:
073: private org.eclipse.swt.graphics.Image swtImage;
074:
075: private Display display;
076:
077: private volatile Object disposeMutex;
078:
079: private volatile Rectangle repaintRequest = null;
080: private final Rectangle ZERO_RECTANGLE = new Rectangle(0, 0, 0, 0);
081:
082: private Object repaintRequestMutex = new Object();
083:
084: private Image buffer;
085:
086: private final int dpi;
087:
088: /**
089: * Create a image that is compatible with this ViewportPane.
090: * <p>
091: * This image is a large block of memory that will be blitted into an SWT Image. Currently this
092: * is BufferedImage.TYPE_4BYTE_ABGR although you should never depend on the Image type directly.
093: * </p>
094: * <p>
095: * This image is *not* expected to be hardware accelarated, althought the blit process will be.
096: * </p>
097: *
098: * @see net.refractions.udig.project.render.ViewportPane#acquireImage(int, int)
099: * @param w width
100: * @param h height
101: * @return BufferedImage with same color model as SWT Image.
102: */
103: public BufferedImage image(int w, int h) {
104: return new BufferedImage(w, h, BufferedImage.TYPE_4BYTE_ABGR);
105: }
106:
107: /**
108: * Creates a new ViewportPaneImpl object.
109: *
110: * @param comp The Composite that this pane will be embedded into
111: * @param renderStack The renderstack that is rendering onto this viewport
112: * @param vmodel The Viewport model that models this viewport
113: */
114: public ViewportPaneSWT(Composite comp, MapEditor editor) {
115: super (comp, SWT.NO_BACKGROUND | SWT.DOUBLE_BUFFERED);
116: dpi = calculateDPI();
117: ProjectUIPlugin.trace(Trace.VIEWPORT, getClass(),
118: "ViewportPaneSWT created", null); //$NON-NLS-1$
119:
120: display = comp.getDisplay();
121: this .editor = editor;
122:
123: addEventListeners();
124: }
125:
126: private void addEventListeners() {
127:
128: handler = new EventHandler(this , eventJob);
129: addListener(SWT.Resize, new Listener() {
130: public void handleEvent(Event event) {
131: Point size = getSize();
132: if (displaySize != null) {
133: if (displaySize.width == size.x
134: && displaySize.height == size.y)
135: return;
136: }
137: displaySize = new Dimension(size.x, size.y);
138: if (buffer != null)
139: buffer.dispose();
140: if (size.x == 0 || size.y == 0)
141: buffer = null;
142: else
143: buffer = new Image(display, size.x, size.y);
144: }
145: });
146: addListener(SWT.MouseDoubleClick, handler);
147: addListener(SWT.MouseDown, handler);
148: addListener(SWT.MouseEnter, handler);
149: addListener(SWT.MouseExit, handler);
150: addListener(SWT.MouseHover, handler);
151: addListener(SWT.MouseMove, handler);
152: addListener(SWT.MouseUp, handler);
153: addListener(SWT.MouseWheel, handler);
154: addListener(SWT.Resize, handler);
155: addListener(SWT.KeyDown, handler);
156: addPaintListener(new PaintListener() {
157: public void paintControl(PaintEvent event) {
158: paint(event.gc, event.display);
159: }
160: });
161: }
162:
163: private int calculateDPI() {
164: Point dpi = getDisplay().getDPI();
165: if (dpi.x != dpi.y)
166: return (dpi.x + dpi.y) / 2;
167: else
168: return dpi.x;
169:
170: }
171:
172: /**
173: * @see net.refractions.udig.project.ui.render.displayAdapter.ViewportPane#setRenderManager(net.refractions.udig.project.render.RenderManager)
174: */
175: public void setRenderManager(RenderManager manager) {
176: this .renderManager = manager;
177: }
178:
179: /**
180: * Called by the Paint listener to update the canvas
181: *
182: * @param display the display of the canvas.
183: * @param g the GC object to use to draw with.
184: */
185: public void paint(GC g, final Display display) {
186: synchronized (repaintRequestMutex) {
187: /*
188: * Refactored for clarity.
189: */
190: if (repaintRequest != ZERO_RECTANGLE) {
191: if (g.getClipping() == null || repaintRequest == null) {
192: repaintRequest = ZERO_RECTANGLE;
193:
194: } else if (g.getClipping().equals(repaintRequest)) {
195: repaintRequest = ZERO_RECTANGLE;
196:
197: } else if (g.getClipping().width == 0
198: && g.getClipping().height == 0) {
199: repaintRequest = ZERO_RECTANGLE;
200:
201: } else if (clipContainsRepaintRequest(g.getClipping())) {
202: repaintRequest = ZERO_RECTANGLE;
203: }
204: }
205: }
206: int antiAliasing;
207: if (ProjectPlugin.getPlugin().getPreferenceStore().getBoolean(
208: PreferenceConstants.P_ANTI_ALIASING))
209: antiAliasing = SWT.ON;
210: else
211: antiAliasing = SWT.OFF;
212: g.setAntialias(antiAliasing);
213: Image swtImage = getImage();
214:
215: int minHeight;
216: int minWidth;
217: if (swtImage == null) {
218: minWidth = displaySize.width;
219: minHeight = displaySize.height;
220: } else {
221: Rectangle bounds = swtImage.getBounds();
222: minWidth = Math.min(bounds.width, getWidth());
223: minHeight = Math.min(bounds.height, getHeight());
224: }
225:
226: getDoubleBufferGraphics(display, g, minWidth, minHeight);
227:
228: synchronized (repaintRequestMutex) {
229: if (repaintRequest != ZERO_RECTANGLE) {
230: Rectangle rect = repaintRequest;
231: repaintRequest = null;
232: doRepaint(rect.x, rect.y, rect.width, rect.height);
233: } else
234: repaintRequest = null;
235: }
236: }
237:
238: private void getDoubleBufferGraphics(final Display display, GC gc,
239: int minWidth, int minHeight) {
240: IPreferenceStore store = UiPlugin.getDefault()
241: .getPreferenceStore();
242: boolean useAdvancedGraphics = store
243: .getBoolean(net.refractions.udig.ui.preferences.PreferenceConstants.P_ADVANCED_GRAPHICS);
244:
245: if ((getStyle() & SWT.DOUBLE_BUFFERED) == 0) {
246: if (buffer == null) {
247: buffer = new Image(display, displaySize.width,
248: displaySize.height);
249: }
250:
251: ViewportGraphics swtGraphics = null;
252:
253: if (useAdvancedGraphics) {
254: swtGraphics = new SWTGraphics(buffer, display);
255: } else {
256: swtGraphics = new NonAdvancedSWTGraphics(buffer,
257: display);
258: }
259:
260: painter.paint(swtGraphics, swtImage, minWidth, minHeight);
261: swtGraphics.dispose();
262:
263: gc.drawImage(buffer, 0, 0);
264: } else {
265:
266: ViewportGraphics swtGraphics = null;
267:
268: if (useAdvancedGraphics) {
269: swtGraphics = new SWTGraphics(gc, display);
270: } else {
271: swtGraphics = new NonAdvancedSWTGraphics(gc, display,
272: null);
273: }
274: painter.paint(swtGraphics, swtImage, minWidth, minHeight);
275: swtGraphics.dispose();
276: }
277: }
278:
279: private boolean clipContainsRepaintRequest(Rectangle clipping) {
280:
281: if (clipping.contains(repaintRequest.x, repaintRequest.y)
282: && clipping.contains(repaintRequest.x
283: + repaintRequest.width, repaintRequest.y)
284: && clipping.contains(repaintRequest.x
285: + repaintRequest.width,
286: repaintRequest.y = repaintRequest.height)
287: && clipping.contains(repaintRequest.x, repaintRequest.y
288: + repaintRequest.height))
289: return true;
290: return false;
291: }
292:
293: /**
294: * Returns buffer image and if necessary creates the new one while disposing the old.
295: *
296: * @return
297: */
298: org.eclipse.swt.graphics.Image getImage() {
299: if (!Platform.isRunning())
300: return null;
301: if (swtImage != null && disposeMutex == null) {
302: return swtImage;
303: }
304:
305: try {
306:
307: if (swtImage != null)
308: swtImage.dispose();
309:
310: disposeMutex = null;
311:
312: swtImage = createImage();
313: return swtImage;
314:
315: } catch (Throwable e) {
316: ProjectUIPlugin.log(null, e);
317: }
318: return null;
319: }
320:
321: /**
322: * Creates the new buffer image.
323: *
324: * @return
325: */
326: private org.eclipse.swt.graphics.Image createImage() {
327: org.eclipse.swt.graphics.Image newImage;
328: RenderedImage image = renderManager.getImage();
329: if (image != null)
330: newImage = SWTGraphics.createSWTImage(image, false);
331: else {
332: newImage = new Image(getDisplay(), getWidth(), getHeight());
333: }
334: return newImage;
335: }
336:
337: /**
338: * Just signals to recreate buffer image in the next redrawing routine.
339: */
340: void initMap() {
341: disposeMutex = new Object();
342: }
343:
344: /**
345: * @see net.refractions.udig.project.ui.render.displayAdapter.ViewportPane#renderStarting()
346: */
347: public void renderStarting() {
348: painter.renderStart();
349: repaint();
350: }
351:
352: /**
353: * @see net.refractions.udig.project.ui.render.displayAdapter.ViewportPane#renderDone()
354: */
355: public void renderDone() {
356: renderUpdate();
357: painter.renderDone();
358: }
359:
360: /**
361: * @see net.refractions.udig.project.ui.render.displayAdapter.ViewportPane#renderUpdate()
362: */
363: public void renderUpdate() {
364: initMap();
365: painter.renderUpdate();
366: repaint();
367: }
368:
369: /**
370: * @see net.refractions.udig.project.render.ViewportPane#setTransform(AffineTransform)
371: */
372:
373: /**
374: * @see net.refractions.udig.project.render.ViewportPane#dispose()
375: */
376: public void dispose() {
377: super .dispose();
378: if (swtImage != null)
379: swtImage.dispose();
380: }
381:
382: /**
383: * @see net.refractions.udig.project.render.ViewportPane#addDrawCommand(net.refractions.udig.project.internal.commands.draw.IDrawCommand)
384: */
385: public void addDrawCommand(IDrawCommand command) {
386: painter.addDrawCommand(command);
387: }
388:
389: /**
390: * @see net.refractions.udig.project.render.ViewportPane#setCursor(java.awt.Cursor)
391: */
392: public void setCursor(final Cursor cursor) {
393: display.asyncExec(new Runnable() {
394: public void run() {
395: String name = cursor.getName();
396: if (name.equals("Default Cursor")) { //$NON-NLS-1$
397: setCursor(new org.eclipse.swt.graphics.Cursor(
398: display, SWT.CURSOR_ARROW));
399: } else if (name.equals("Crosshair Cursor")) { //$NON-NLS-1$
400: setCursor(new org.eclipse.swt.graphics.Cursor(
401: display, SWT.CURSOR_CROSS));
402: } else if (name.equals("Text Cursor")) { //$NON-NLS-1$
403: setCursor(new org.eclipse.swt.graphics.Cursor(
404: display, SWT.CURSOR_IBEAM));
405: } else if (name.equals("Wait Cursor")) { //$NON-NLS-1$
406: setCursor(new org.eclipse.swt.graphics.Cursor(
407: display, SWT.CURSOR_WAIT));
408: } else if (name.equals("SW Resize Cursor")) { //$NON-NLS-1$
409: setCursor(new org.eclipse.swt.graphics.Cursor(
410: display, SWT.CURSOR_SIZESW));
411: } else if (name.equals("SE Resize Cursor")) { //$NON-NLS-1$
412: setCursor(new org.eclipse.swt.graphics.Cursor(
413: display, SWT.CURSOR_SIZESE));
414: } else if (name.equals("NW Resize Cursor")) { //$NON-NLS-1$
415: setCursor(new org.eclipse.swt.graphics.Cursor(
416: display, SWT.CURSOR_SIZENW));
417: } else if (name.equals("NE Resize Cursor")) { //$NON-NLS-1$
418: setCursor(new org.eclipse.swt.graphics.Cursor(
419: display, SWT.CURSOR_SIZENE));
420: } else if (name.equals("N Resize Cursor")) { //$NON-NLS-1$
421: setCursor(new org.eclipse.swt.graphics.Cursor(
422: display, SWT.CURSOR_SIZEN));
423: } else if (name.equals("S Resize Cursor")) { //$NON-NLS-1$
424: setCursor(new org.eclipse.swt.graphics.Cursor(
425: display, SWT.CURSOR_SIZES));
426: } else if (name.equals("W Resize Cursor")) { //$NON-NLS-1$
427: setCursor(new org.eclipse.swt.graphics.Cursor(
428: display, SWT.CURSOR_SIZEW));
429: } else if (name.equals("E Resize Cursor")) { //$NON-NLS-1$
430: setCursor(new org.eclipse.swt.graphics.Cursor(
431: display, SWT.CURSOR_SIZEE));
432: } else if (name.equals("Hand Cursor")) { //$NON-NLS-1$
433: setCursor(new org.eclipse.swt.graphics.Cursor(
434: display, SWT.CURSOR_HAND));
435: } else if (name.equals("Move Cursor")) { //$NON-NLS-1$
436: setCursor(new org.eclipse.swt.graphics.Cursor(
437: display, SWT.CURSOR_SIZEALL));
438: }
439: }
440: });
441: }
442:
443: /**
444: * @see net.refractions.udig.project.render.ViewportPane#removeMouseListener(net.refractions.udig.project.render.MapMouseListener)
445: */
446: public void removeMouseListener(MapMouseListener l) {
447: eventJob.removeMouseListener(l);
448: }
449:
450: /**
451: * @see net.refractions.udig.project.render.ViewportPane#removeMouseMotionListener(net.refractions.udig.project.render.MapMouseMotionListener)
452: */
453: public void removeMouseMotionListener(MapMouseMotionListener l) {
454: eventJob.removeMouseMotionListener(l);
455: }
456:
457: /**
458: * @see net.refractions.udig.project.render.ViewportPane#removeMouseWheelListener(net.refractions.udig.project.render.MapMouseWheelListener)
459: */
460: public void removeMouseWheelListener(MapMouseWheelListener l) {
461: eventJob.removeMouseWheelListener(l);
462: }
463:
464: /**
465: * @see net.refractions.udig.project.render.ViewportPane#addMouseListener(net.refractions.udig.project.render.MapMouseListener)
466: */
467: public void addMouseListener(MapMouseListener l) {
468: eventJob.addMouseListener(l);
469: }
470:
471: /**
472: * @see net.refractions.udig.project.render.ViewportPane#addMouseMotionListener(net.refractions.udig.project.render.MapMouseMotionListener)
473: */
474: public void addMouseMotionListener(MapMouseMotionListener l) {
475: eventJob.addMouseMotionListener(l);
476: }
477:
478: /**
479: * @see net.refractions.udig.project.render.ViewportPane#addMouseWheelListener(net.refractions.udig.project.render.MapMouseWheelListener)
480: */
481: public void addMouseWheelListener(MapMouseWheelListener l) {
482: eventJob.addMouseWheelListener(l);
483: }
484:
485: /**
486: * @see net.refractions.udig.project.render.MapDisplay#getDisplaySize()
487: */
488: public Dimension getDisplaySize() {
489: return displaySize;
490: }
491:
492: /**
493: * @see net.refractions.udig.project.render.MapDisplay#getWidth()
494: */
495: public int getWidth() {
496: return getDisplaySize().width;
497: }
498:
499: /**
500: * @see net.refractions.udig.project.render.MapDisplay#getHeight()
501: */
502: public int getHeight() {
503: return getDisplaySize().height;
504: }
505:
506: /**
507: * @see net.refractions.udig.project.ui.render.displayAdapter.ViewportPane#addPaneListener(net.refractions.udig.project.render.displayAdapter.MapDisplayListener)
508: */
509: public void addPaneListener(IMapDisplayListener listener) {
510: eventJob.addMapEditorListener(listener);
511: }
512:
513: /**
514: * @see net.refractions.udig.project.ui.render.displayAdapter.ViewportPane#removePaneListener(net.refractions.udig.project.render.displayAdapter.MapDisplayListener)
515: */
516: public void removePaneListener(IMapDisplayListener listener) {
517: eventJob.removeMapEditorListener(listener);
518: }
519:
520: /**
521: * @see net.refractions.udig.project.ui.render.displayAdapter.ViewportPane#getMapEditor()
522: */
523: public MapEditor getMapEditor() {
524: return editor;
525: }
526:
527: public int getDPI() {
528: return dpi;
529: }
530:
531: public Control getControl() {
532: return this ;
533: }
534:
535: public void repaint(int x, int y, int width, int height) {
536: if (width == 0 || height == 0)
537: return;
538:
539: synchronized (repaintRequestMutex) {
540: Rectangle rectangle = new Rectangle(x, y, width, height);
541: if (repaintRequest == null) {
542: repaintRequest = rectangle;
543: doRepaint(x, y, width, height);
544: } else if (repaintRequest == ZERO_RECTANGLE) {
545: repaintRequest = rectangle;
546: } else {
547: repaintRequest.union(rectangle);
548: }
549: }
550:
551: }
552:
553: /**
554: * @param x
555: * @param y
556: * @param width
557: * @param height
558: */
559: private void doRepaint(int x, int y, int width, int height) {
560: REPAINT.x = x;
561: REPAINT.y = y;
562: REPAINT.width = width;
563: REPAINT.height = height;
564: if (Display.getCurrent() != null) {
565: REPAINT.run();
566: } else
567: display.asyncExec(REPAINT);
568: }
569:
570: /**
571: * @see net.refractions.udig.project.render.ViewportPane#repaint()
572: */
573: public void repaint() {
574: repaint(0, 0, displaySize.width, displaySize.height);
575: }
576:
577: private class Repainter implements Runnable {
578: int x;
579: int y;
580: int width;
581: int height;
582:
583: public void run() {
584: if (PlatformUI.getWorkbench().isClosing())
585: return;
586: if (!isDisposed()) {
587: redraw(x, y, width, height, false);
588: }
589: }
590:
591: }
592:
593: public void enableDrawCommands(boolean enable) {
594: painter.switchOnOff(enable);
595: }
596:
597: @Override
598: public void setCursor(org.eclipse.swt.graphics.Cursor cursor) {
599: super.setCursor(cursor);
600: }
601: }
|