#include <Wt/WBrush>
#include <Wt/WColor>
#include <Wt/WContainerWidget>
#include <Wt/WPaintDevice>
#include <Wt/WPaintedWidget>
#include <Wt/WPainter>

class StyleWidget : public Wt::WPaintedWidget
{
public:
    StyleWidget(Wt::WContainerWidget *parent = 0)
	: Wt::WPaintedWidget(parent)
    {
	resize(310, 1140);  // Provide a default size.
    }

protected:
    void paintEvent(Wt::WPaintDevice *paintDevice) {
	Wt::WPainter painter(paintDevice);

	// Draw a grid of rectangles; each one in a different color.
	for (int row = 0; row < 6; row++)
	    for (int col = 0; col < 6; col++) {
		// Generate a unique RGB color for each square. Only the red and
		// green values are modified; the blue channel has a fixed value.
		Wt::WBrush brush(Wt::WColor(255 - 42*row, 255 - 42*col, 0));
		painter.fillRect(row*25, col*25, 25, 25, brush);
	    }

	painter.translate(0, 160);
	// Draw a grid of circles similar to the above example but now using the
	// strokePath method.
	Wt::WPen pen;
	pen.setWidth(3);
	for (int row = 0; row < 6; row++) {
	    for (int col = 0; col < 6; col++) {
		// Generate a unique RGB color for each circle. Only the green and
		// blue values are modified; the red channel has a fixed value.
		Wt::WPainterPath path;
		path.addEllipse(3 + col*25, 3 + row*25, 20, 20);
		pen.setColor(Wt::WColor(0, 255 - 42*row, 255 - 42*col));
		painter.strokePath(path, pen);
	    }
	}

	painter.translate(0, 160);
	// Transparency example with rectangles
	// Create a background composed of 4 stacked rectangles.
	painter.fillRect(0, 0, 150, 37.5, Wt::WBrush(Wt::yellow));
	painter.fillRect(0, 37.5, 150, 37.5, Wt::WBrush(Wt::green));
	painter.fillRect(0, 75, 150, 37.5, Wt::WBrush(Wt::blue));
	painter.fillRect(0, 112.5, 150, 37.5, Wt::WBrush(Wt::red));
	// On top of these draw semi transparent rectangles with increasing opacity
	for (int i = 0; i < 10; i++) {
	    Wt::WBrush brush = Wt::WBrush(Wt::WColor(255, 255, 255, 255/10*i));
	    for (int j = 0; j < 4; j++) {
		Wt::WPainterPath path;
		path.addRect(5 + i*14, 5 + j*37.5, 14, 27.5);
		painter.fillPath(path, brush);
	    }
	}

	painter.translate(0, 160);
	// Transparency example with circles
	// Create a square composed of four different colored squares.
	painter.fillRect(0, 0, 75, 75, Wt::WBrush(Wt::yellow));
	painter.fillRect(75, 0, 75, 75, Wt::WBrush(Wt::green));
	painter.fillRect(0, 75, 75, 75, Wt::WBrush(Wt::blue));
	painter.fillRect(75, 75, 75, 75, Wt::WBrush(Wt::red));

	// On top of these draw a set of semi-transparant white circles with
	// increasing diameter. The final result is a radial gradient.
	for (int i = 1; i < 8; i++) {
	    Wt::WPainterPath path;
	    path.addEllipse(75 - i*10, 75 - i*10, i*20, i*20);
	    Wt::WBrush brush = Wt::WBrush(Wt::WColor(255, 255, 255, 50));
	    painter.fillPath(path, brush);
	}
	
	painter.translate(0, 170);
	// Gradient Brush example
	// Rectangle with a linear gradient from left top to right bottom
	painter.setPen(Wt::WPen(Wt::NoPen));
	Wt::WGradient linGrad;
	linGrad.setLinearGradient(0, 0, 100, 150);
	linGrad.addColorStop(0, Wt::WColor(255, 0, 0, 255));
	linGrad.addColorStop(0.5, Wt::WColor(0, 0, 255, 255));
	linGrad.addColorStop(1, Wt::WColor(0, 255, 0, 255));
	Wt::WBrush linearGradientBrush(linGrad);
	painter.setBrush(linearGradientBrush);
	painter.drawRect(0, 0, 100, 150);

	// Circle with a radial gradient
	Wt::WGradient radGrad;
	radGrad.setRadialGradient(170, 100, 50, 130, 130);
	radGrad.addColorStop(0.2, Wt::WColor(255, 0, 0, 255));
	radGrad.addColorStop(0.9, Wt::WColor(0, 0, 255, 255));
	radGrad.addColorStop(1, Wt::WColor(0, 0, 255, 0));
	Wt::WBrush radialGradientBrush(radGrad);
	painter.setBrush(radialGradientBrush);
	painter.drawEllipse(120, 50, 100, 100);

	painter.translate(0, 170);
	// LineWidth example

	// You can use WPainter::drawLine() or WPainter::strokePath() to draw a
	// line. Using strokePath() you can draw thicker lines.

	// The line is centered on the path. In other words, the area that's drawn
	// extends to half the line width on either side of the path. Because
	// canvas coordinates do not directly reference pixels, you have to take
	// special care to obtain crisp horizontal and vertical lines.

	// All lines with an odd integer width thickness in the example below do
	// not appear crisp, because of the path's positioning.
	for (int i = 0; i < 11; i++) {
	    Wt::WPainterPath path;
	    path.moveTo(i*14, 0);
	    path.lineTo(i*14, 150);
	    pen = Wt::WPen();
	    pen.setWidth(i+1);
	    painter.strokePath(path, pen);
	}

	painter.translate(160, 0);

	// LineWidth example with crisp lines

	// To obtain a crisp line for an odd integer width thickness line you have
	// to be very precise in your path creation, e.g. a 1.0 width line will
	// extend half a unit to either side of the path.
	for (int i = 0; i < 11; i++) {
	    Wt::WPainterPath path;
	    if (i % 2 == 0) {
		path.moveTo(i*14-0.5, 0);
		path.lineTo(i*14-0.5, 150);
	    } else {
		path.moveTo(i*14, 0);
		path.lineTo(i*14, 150);
	    }

	    pen = Wt::WPen();
	    pen.setCapStyle(Wt::FlatCap);   // Now, all lines will have equal length.
	    pen.setWidth(i+1);
	    painter.strokePath(path, pen);
	}

	painter.translate(-160, 170);

	// PenCapStyle example
	// The PenCapStyle can be FlatCap, SquareCap or RoundCap.
	// Start with drawing guides:
	Wt::WPainterPath guidePath;
	guidePath.moveTo(0, 10);
	guidePath.lineTo(150,10);
	guidePath.moveTo(0,140);
	guidePath.lineTo(150,140);
	pen = Wt::WPen(Wt::blue);
	painter.strokePath(guidePath, pen);

	// Draw lines with different cap styles
	// Create three parallel paths:
	std::vector<Wt::WPainterPath> paths;
	for (int i = 0; i < 3; i++) {
	    Wt::WPainterPath path;
	    path.moveTo(25+i*50, 10);
	    path.lineTo(25+i*50, 140);
	    paths.push_back(path);
	}

	pen = Wt::WPen();
	pen.setWidth(20);
	pen.setCapStyle(Wt::FlatCap);
	painter.strokePath(paths[0], pen);

	pen = Wt::WPen();
	pen.setWidth(20);
	pen.setCapStyle(Wt::SquareCap);
	painter.strokePath(paths[1], pen);

	pen = Wt::WPen();
	pen.setWidth(20);
	pen.setCapStyle(Wt::RoundCap);
	painter.strokePath(paths[2], pen);

	painter.translate(0, 170);

	// PenJoinStyle example
	// The PenJoinStyle can be MiterJoin, BevelJoin or RoundJoin.
	// Create three parallel paths:

	paths.clear();
	for (int i = 0; i < 3; i++) {
	    Wt::WPainterPath path;
	    path.moveTo(15,  5+i*40);
	    path.lineTo(45, 45+i*40);
	    path.lineTo(75,  5+i*40);
	    path.lineTo(105,45+i*40);
	    path.lineTo(135, 5+i*40);
	    paths.push_back(path);
	}

	// Draw the first path with miter joins.
	// The connected segments are joined by extending their outside edges to
	// connect at a single point.
	pen = Wt::WPen();
	pen.setWidth(20);
	pen.setJoinStyle(Wt::MiterJoin);
	painter.strokePath(paths[0], pen);

	// Draw the second path with bevel joins.
	// An additional triangular area is filled between the common endpoint of
	// connected segments and the separate outside rectangular corners of each
	// segment.
	pen = Wt::WPen();
	pen.setWidth(20);
	pen.setJoinStyle(Wt::BevelJoin);
	painter.strokePath(paths[1], pen);

	// Draw the third path with round joins.
	// The corners of the shape are rounded off by filling an aditonal sector
	// of disc centered at the common endpoint of connected segments. The
	// radius of the rounded corners is equal to the line width.
	pen = Wt::WPen();
	pen.setWidth(20);
	pen.setJoinStyle(Wt::RoundJoin);
	painter.strokePath(paths[2], pen);
    }
};

SAMPLE_BEGIN(PaintingStyle)
Wt::WContainerWidget *container = new Wt::WContainerWidget();

new StyleWidget(container);

SAMPLE_END(return container)
