SeatingCanvas.java

/*
 * Copyright © 2011 Nokia Corporation. All rights reserved.
 * Nokia and Nokia Connecting People are registered trademarks of Nokia Corporation. 
 * Oracle and Java are trademarks or registered trademarks of Oracle and/or its
 * affiliates. Other product and company names mentioned herein may be trademarks
 * or trade names of their respective owners. 
 * See LICENSE.TXT for license information.
 */
package com.nokia.example.moviebooking.ui;

import com.nokia.example.moviebooking.moviedb.Showing;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.ControlListener;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Shell;

/**
 * Contains the seat distribution and facilities to select it
 */
class SeatingCanvas extends Canvas
        implements PaintListener, ControlListener, KeyListener, MouseListener {

    private Showing showing;
    private int[][] selected;
    private Image doubleBuffer;
    private GC gc;
    private boolean[][] seats;
    private int seatCount;
    private int selectedRow, firstSelectedSeat;
    private int rows;
    private int seatsPerRow;
    private Shell shell;
    private Point seatSize;
    private Rectangle screenRectangle;

    SeatingCanvas(Shell shell, Showing showing,
            int[][] selectedSeating, int seatCount) {
        super(shell, SWT.NO_BACKGROUND);
        this.showing = showing;
        this.shell = shell;
        this.seatCount = seatCount;
        this.selected = selectedSeating;
        seats = showing.getSeating();
        rows = seats.length;
        seatsPerRow = seats[0].length;

        init();
    }

    void init() {
        // Select the first free seats
        selected = new int[seatCount][2];
        for (int i = 0; i < rows; i++) {
            if (searchSeatsInRow(i, 0)) {
                selectedRow = i;
                break;
            }
        }
        // This is necessary to repaint the screen
        addPaintListener(this);
        // Request to listen for keys
        addKeyListener(this);
        //Listen for touch events
        addMouseListener(this);
        // Listen if the size of the parent changes
        shell.addControlListener(this);

    }

    public void keyPressed(KeyEvent e) {
        // Nothing to do, we are only interested in key releases
    }

    public void keyReleased(KeyEvent e) {
        // Check if the arrow keys are pressed
        if (e.keyCode == SWT.ARROW_UP) {
            if (selectedRow > 0) {
                searchSeatsInRow(selectedRow - 1, firstSelectedSeat);
                redraw();
            }
        } else if (e.keyCode == SWT.ARROW_DOWN) {
            if (selectedRow < (rows - 1)) {
                searchSeatsInRow(selectedRow + 1, firstSelectedSeat);
                redraw();
            }
        } else if (e.keyCode == SWT.ARROW_LEFT) {
            if (firstSelectedSeat > 0) {
                searchSeatsInRow(selectedRow, firstSelectedSeat - 1);
                redraw();
            }
        } else if (e.keyCode == SWT.ARROW_RIGHT) {
            if (firstSelectedSeat < (seatsPerRow - seatCount)) {
                searchSeatsInRow(selectedRow, firstSelectedSeat + 1);
                redraw();
            }
        }
    }

    public void paintControl(PaintEvent e) {
        //  Dump the offscreen image onto the canvas
        drawSeating();
        e.gc.drawImage(doubleBuffer, 0, 0);
    }

    public void controlMoved(ControlEvent e) {
        // We are not interested in this event
    }

    public void controlResized(ControlEvent e) {
        // Recreate the double buffer with the new size
        if (doubleBuffer != null) {
            doubleBuffer.dispose();
        }
        doubleBuffer = null;
        if (gc != null) {
            gc.dispose();
        }
        gc = null;

        redraw();
    }

    // Returns the currently selected seats
    int[][] getSelectedSeats() {
        return selected;
    }

    public void destroy() {
        if (doubleBuffer != null) {
            doubleBuffer.dispose();
        }
        if (gc != null) {
            gc.dispose();
        }
    }

    // Search if seatCount seats are available in row i
    private boolean searchSeatsInRow(int i, int firstSelectedSeat) {
        int k = 0;
        for (int j = firstSelectedSeat; j < seatsPerRow; j++) {
            if (!seats[i][j]) {
                k++;
            }
        }
        if (k >= seatCount) {
            k = 0;
            for (int j = firstSelectedSeat; j < seatsPerRow
                    && k < seatCount; j++) {
                if (!seats[i][j]) {
                    selected[k][0] = i;
                    selected[k++][1] = j;
                }
            }
            selectedRow = i;
            this.firstSelectedSeat = selected[0][1];
            return true;
        } else {
            return false;
        }
    }

    // Drawing of the offscreen image
    private void drawSeating() {

        Color bgColor = getDisplay().getSystemColor(SWT.COLOR_WHITE);
        Color occupiedSeatColor = getDisplay().getSystemColor(SWT.COLOR_RED);
        Color availableSeatColor = getDisplay().getSystemColor(SWT.COLOR_BLUE);
        Color frameColor = getDisplay().getSystemColor(SWT.COLOR_BLACK);
        Color selectedSeatColor = getDisplay().getSystemColor(SWT.COLOR_GREEN);
        Color screenColor = getDisplay().getSystemColor(SWT.COLOR_GRAY);

        if (doubleBuffer == null) {
            doubleBuffer = new Image(getDisplay(), getBounds());
        }
        if (gc == null) {
            gc = new GC(doubleBuffer);
        }

        Rectangle screen = getScreen();

        // Clear the image
        /*Point canvasSize = new Point(shell.getClientArea().width,
        shell.getClientArea().height);
         */
        Point canvasSize = getSize();
        gc.setBackground(bgColor);
        gc.fillRectangle(0, 0, canvasSize.x, canvasSize.y);

        gc.setForeground(frameColor);
        gc.drawRectangle(1, 1, canvasSize.x - 2, canvasSize.y - 2);

        gc.setForeground(screenColor);
        gc.setBackground(screenColor);
        gc.fillRectangle(screen);

        boolean[][] seats = showing.getSeating();
        int rows = seats.length;
        int seatsPerRow = seats[0].length;
        int radius = getRadius(rows, seatsPerRow);
        // Space per seat/row
        Point seatSize = getSeatSize(rows, seatsPerRow);

        // Empty space to leave for horizontal center alignment
        int xOffset = getOffsetX(screen, seatsPerRow, seatSize);
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < seatsPerRow; j++) {
                if (seats[i][j]) {
                    gc.setBackground(occupiedSeatColor);
                } else {
                    gc.setBackground(availableSeatColor);
                }
                gc.fillOval(screen.x + xOffset + j * seatSize.x, (screen.y
                        + screen.height + radius / 2)
                        + i * seatSize.y, radius, radius);
            }
        }
        for (int i = 0; i < selected.length; i++) {
            gc.setBackground(selectedSeatColor);
            System.out.println("selected[i][1]" + selected[i][1]);
            System.out.println("selected[i][0]" + selected[i][0]);
            gc.fillOval(screen.x + xOffset + selected[i][1] * seatSize.x, (screen.y
                    + screen.height + radius / 2)
                    + selected[i][0] * seatSize.y, radius, radius);
        }
    }

    private int getOffsetX(Rectangle screen, int seatsPerRow, Point seatSize) {
        int xOffset = (screen.width - seatSize.x * seatsPerRow) / 2;
        return xOffset;
    }

    private Rectangle getScreen() {

        Point canvasSize = getSize();
        int width = (95 * canvasSize.x) / 100;
        int height = canvasSize.y / 20;
        screenRectangle = new Rectangle(
                (canvasSize.x - width) / 2,
                height,
                width,
                height);

        return screenRectangle;
    }

    /**
     * Calculate the seat per size/row
     *
     * @param canvasSize
     * @param screen
     * @param rows
     * @param seatsPerRow
     * @param radius
     * @return
     */
    private Point getSeatSize(int rows,
            int seatsPerRow) {

        int radius = getRadius(rows, seatsPerRow);
        Rectangle screen = getScreen();
        int spacex = screen.width / seatsPerRow;
        int spacey = ((getSize().y - (screen.height + screen.y + radius / 2)) / rows);
        seatSize = new Point(spacex, spacey);

        return seatSize;
    }

    private int getRadius(int rows, int seatsPerRow) {
        Rectangle screen = getScreen();
        int radius = Math.min((screen.width / seatsPerRow),
                ((getSize().y - (screen.height + screen.y)) / rows)) - 2;
        return radius;
    }

    public void mouseDoubleClick(MouseEvent event) {
        // Nothing to do
    }

    public void mouseDown(MouseEvent event) {
        handleMouse(event);

    }

    public void mouseUp(MouseEvent event) {
        handleMouse(event);
    }

    private void handleMouse(MouseEvent event) {
        Rectangle screen = getScreen();
        Point seatSize = getSeatSize(seats.length, seats[0].length);
        if (event.y > (screen.y + screen.height)) {
            int col = (event.x) / seatSize.x;
            if (col < 1) {
                col = 1;
            }
            if (col > seats[0].length) {
                col = seats[0].length;
            }
            int row = (event.y - screen.y) / seatSize.y;
            if (row < 1) {
                row = 1;
            }
            if (row > seats.length) {
                row = seats.length;
            }

            for (int seat = 0; seat < seatCount && col < seatsPerRow; col++) {
                if (!seats[row - 1][col - 1]) {
                    selected[seat][0] = row - 1;
                    selected[seat][1] = col - 1;
                    seat++;
                }

            }
            redraw();
        }

    }
}