/*******************************************************************************
 * Copyright (c) 2000, 2005 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.draw2d;

import org.eclipse.swt.SWT;
import org.eclipse.swt.SWTError;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Caret;
import org.eclipse.swt.widgets.Control;

import org.eclipse.draw2d.geometry.Rectangle;

class BufferedGraphicsSource implements GraphicsSource {

private Image imageBuffer;
private GC imageGC;
private GC controlGC;
private Control control;
private Rectangle inUse;

/**
 * Constructs a new buffered graphics source using the given control.
 * @since 2.1
 * @param c the control
 */
public BufferedGraphicsSource(Control c) {
	control = c;
}

/**
 * @see org.eclipse.draw2d.GraphicsSource#flushGraphics(org.eclipse.draw2d.geometry.Rectangle)
 */
public void flushGraphics(Rectangle region) {
	if (inUse.isEmpty())
		return;
	
	boolean restoreCaret = false;
	Canvas canvas = null;
	if (control instanceof Canvas) {
		canvas = (Canvas)control;
		Caret caret = canvas.getCaret();
		if (caret != null)
			restoreCaret = caret.isVisible();
		if (restoreCaret)
			caret.setVisible(false);
	}
	/*
	 * The imageBuffer may be null if double-buffering was not successful.
	 */
	if (imageBuffer != null) {
		imageGC.dispose();
		controlGC.drawImage(getImage(),
				0, 0, inUse.width, inUse.height,
				inUse.x, inUse.y, inUse.width, inUse.height);
		imageBuffer.dispose();
		imageBuffer = null;
		imageGC = null;
	}
	controlGC.dispose();
	controlGC = null;
	
	if (restoreCaret)
		canvas.getCaret().setVisible(true);
}

/**
 * @see org.eclipse.draw2d.GraphicsSource#getGraphics(org.eclipse.draw2d.geometry.Rectangle)
 */
public Graphics getGraphics(Rectangle region) {
	if (control == null || control.isDisposed())
		return null;

	org.eclipse.swt.graphics.Point ptSWT = control.getSize();
	inUse = new Rectangle(0, 0, ptSWT.x, ptSWT.y);
	inUse.intersect(region);
	if (inUse.isEmpty())
		return null;
	
	/*
	 * Bugzilla 53632 - Attempts to create large images on some platforms will fail.
	 * When this happens, do not use double-buffering for painting.
	 */
	try {
		imageBuffer = new Image(null, inUse.width, inUse.height);
	} catch (SWTError noMoreHandles) {
		imageBuffer = null;
	} catch (IllegalArgumentException tooBig) {
		imageBuffer = null;
	}
	
	controlGC = new GC(control,
			control.getStyle() & (SWT.RIGHT_TO_LEFT | SWT.LEFT_TO_RIGHT));
	Graphics graphics;
	if (imageBuffer != null) {
		imageGC = new GC(imageBuffer,
				control.getStyle() & (SWT.RIGHT_TO_LEFT | SWT.LEFT_TO_RIGHT));
		imageGC.setBackground(controlGC.getBackground());
		imageGC.setForeground(controlGC.getForeground());
		imageGC.setFont(controlGC.getFont());
		imageGC.setLineStyle(controlGC.getLineStyle());
		imageGC.setLineWidth(controlGC.getLineWidth());
		imageGC.setXORMode(controlGC.getXORMode());
		graphics = new SWTGraphics(imageGC);
		graphics.translate(inUse.getLocation().negate());
	} else {
		graphics = new SWTGraphics(controlGC);
	}

	graphics.setClip(inUse);
	return graphics;
}

/**
 * Returns the current image buffer or <code>null</code>.
 * @since 2.1
 * @return the current image buffer
 */
protected Image getImage() {
	return imageBuffer;
}

/**
 * Returns the current GC used on the buffer or <code>null</code>.
 * @since 2.1
 * @return the GC for the image buffer
 */
protected GC getImageGC() {
	return imageGC;
}

}
