blob: fec929bfd5b73b067b0cdc4bebf302a4840f387d [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2012, 2013 Tilera Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* William R. Swanson (Tilera Corporation)
* Marc Dumais (Ericsson) - Bug 399281
*******************************************************************************/
package org.eclipse.cdt.visualizer.ui.canvas;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.ControlListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
// ---------------------------------------------------------------------------
// BufferedCanvas
// ---------------------------------------------------------------------------
/** Canvas control with double-buffering support. */
public class BufferedCanvas extends Canvas implements PaintListener, ControlListener {
// --- members ---
/** double-buffering image */
protected Image m_doubleBuffer = null;
/** buffer GC */
protected GC m_doubleBufferGC = null;
// --- constructors/destructors ---
/** Constructor. */
public BufferedCanvas(Composite parent) {
super(parent, SWT.NO_BACKGROUND | // don't automatically clear background on paint event
SWT.NO_REDRAW_RESIZE // don't automatically repaint on resize event
);
initBufferedCanvas();
}
/** Dispose method. */
@Override
public void dispose() {
super.dispose();
cleanupBufferedCanvas();
}
// --- init methods ---
/** Initializes control. */
protected void initBufferedCanvas() {
addControlListener(this);
addPaintListener(this);
}
/** Cleans up control. */
protected void cleanupBufferedCanvas() {
if (!this.isDisposed()) {
removePaintListener(this);
removeControlListener(this);
}
if (m_doubleBuffer != null) {
m_doubleBuffer.dispose();
m_doubleBuffer = null;
}
}
// --- event handlers ---
/** Invoked when control is moved/resized */
@Override
public void controlMoved(ControlEvent e) {
// do nothing, we don't care
}
/** Invoked when control is resized */
@Override
public void controlResized(ControlEvent e) {
resized(getBounds());
}
// --- resize methods ---
/** Invoked when control is resized.
* Default implementation does nothing,
* intended to be overridden by derived types.
*/
public void resized(Rectangle bounds) {
}
// --- GC management ---
/** Gets/creates GC for current background buffer.
* NOTE: The GC is disposed whenever the canvas size changes,
* so caller should not retain a reference to this GC.
*/
protected synchronized GC getBufferedGC() {
if (m_doubleBufferGC == null) {
m_doubleBufferGC = new GC(m_doubleBuffer);
}
return m_doubleBufferGC;
}
/** Disposes of current background buffer GC. */
protected synchronized void disposeBufferedGC() {
if (m_doubleBufferGC != null) {
m_doubleBufferGC.dispose();
m_doubleBufferGC = null;
}
}
// --- paint methods ---
/** Invoked when control needs to be repainted */
@Override
public void paintControl(PaintEvent e) {
// Handle last paint event of a cluster.
if (e.count <= 1) {
Display display = e.display;
GC gc = e.gc;
paintDoubleBuffered(display, gc);
}
}
/** Internal -- handles double-buffering support, calls paintCanvas() */
// NOTE: need display to create image buffer, not for painting code
protected void paintDoubleBuffered(Display display, GC gc) {
// get/create background image buffer
Rectangle clientArea = getClientArea();
int width = clientArea.width;
int height = clientArea.height;
if (m_doubleBuffer == null || m_doubleBuffer.getBounds().width < width
|| m_doubleBuffer.getBounds().height < height) {
m_doubleBuffer = new Image(display, width, height);
disposeBufferedGC();
}
// create graphics context for buffer
GC bgc = getBufferedGC();
// copy current GC properties into it as defaults
bgc.setBackground(gc.getBackground());
bgc.setForeground(gc.getForeground());
bgc.setFont(gc.getFont());
bgc.setAlpha(255);
// invoke paintCanvas() method to paint into the buffer
try {
paintCanvas(bgc);
} catch (Throwable t) {
// Throwing an exception in painting code can hang Eclipse,
// so catch any exceptions here.
System.err.println("BufferedCanvas: Exception thrown in painting code: \n"); //$NON-NLS-1$
t.printStackTrace(System.err);
}
// then copy image buffer to actual canvas (reduces repaint flickering)
gc.drawImage(m_doubleBuffer, 0, 0);
}
/** Invoked when canvas repaint event is raised.
* Default implementation clears canvas to background color.
*/
public void paintCanvas(GC gc) {
clearCanvas(gc);
}
/** Clears canvas to background color. */
public void clearCanvas(GC gc) {
Rectangle bounds = getClientArea();
gc.fillRectangle(bounds);
}
// --- update methods ---
/** Redraws control */
@Override
public void update() {
// guard against update events that happen
// after app has shut down
if (!isDisposed()) {
redraw();
}
}
}