Implementing the UI elements

MoviesListScreen

The MoviesListScreen class provides the user interface for the list of movies and displays the list to the user. The class organizes the information into two adjacent panels using the landscape style layout.

Figure 40: The main screen of the MIDlet

  1. Create the MoviesListScreen class file.

  2. Import the required classes and assign this class to the package moviebooking.ui.

    package moviebooking.ui;
    
    import java.io.InputStream;
    import java.io.IOException;
    
    import moviebooking.moviedb.Movie;
    
    import org.eclipse.ercp.swt.mobile.Command;
    import org.eclipse.swt.SWT;
    import org.eclipse.swt.events.SelectionEvent;
    import org.eclipse.swt.events.SelectionListener;
    import org.eclipse.swt.graphics.Image;
    import org.eclipse.swt.layout.FormAttachment;
    import org.eclipse.swt.layout.FormData;
    import org.eclipse.swt.layout.FormLayout;
    import org.eclipse.swt.layout.GridData;
    import org.eclipse.swt.widgets.Composite;
    import org.eclipse.swt.widgets.Label;
    import org.eclipse.swt.widgets.List;
    import org.eclipse.swt.widgets.Shell;
    /**
     * This class takes the list of movies and displays it to the user
     * The list of movies is shown at the right and a description of each
     * at the left.
     */
    
    class MoviesListScreen implements SelectionListener {
    
    	private Command reserveMovieCommand, exitCommand;
    	private MovieBooking main;
    	private List moviesList;
    	private java.util.Vector movies;
    	private Composite mainComposite;
    	private Image image;
    	private Label imageLabel;
    	private Label descriptionText;
    
  3. Create a Shell. For more information the Shell class, see the class Shell.

      MoviesListScreen(MovieBooking main, Shell mainShell, java.util.Vector movies) {
    	    this.main = main;
    	    this.movies = movies;
        
    	    mainShell.setText("Movies");
        
        	mainComposite = new Composite(mainShell, SWT.NO_BACKGROUND);
    
    		// Size and location of this Composite are controlled by 
    		// the GridLayout of the mainShell. 
    		mainComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
    		mainShell.layout();
    
    		// this contains the list of movies
    		moviesList = new List(mainComposite, SWT.BORDER);
  4. Create widgets for the screen. For a full list of widgets offered and additional information, see the package org.eclipse.swt.widgets .

    		// This contains the list of movies
    		moviesList = new List(mainComposite, SWT.BORDER);
    		
    		// Widgets to describe the movie
    		imageLabel = new Label(mainComposite, SWT.TOP | SWT.CENTER);
    		descriptionText = new Label(mainComposite, SWT.WRAP | SWT.CENTER);
    
    		// Populate moviesList
    		for(int i = 0; i < movies.size(); ++i) {
    			Movie m = (Movie)movies.elementAt(i);
    			moviesList.add(m.getName());
    		}
    		if (movies.size() > 0) {
    			moviesList.select(0);
    			movieSelected(0);
    		}
    		moviesList.addSelectionListener(this);
  5. Organize the widgets by using a layout. For a full list of layouts offered and additional information, see the package org.eclipse.swt.layout.

    		// Distribute the widgets using a FormLayout
    		FormLayout layout = new FormLayout();
    		mainComposite.setLayout(layout);
    
    		// Set movies list location
    		FormData moviesListData = new FormData();
    		moviesListData.top = new FormAttachment(0);
    		moviesListData.left = new FormAttachment(0);
    		moviesListData.right = new FormAttachment(50);
    		moviesListData.bottom = new FormAttachment(100);
    		moviesList.setLayoutData(moviesListData);
    
    		// Set image location
    		FormData imageLabelData = new FormData();
    		imageLabelData.top = new FormAttachment(moviesList, 0, SWT.TOP);
    		imageLabelData.left = new FormAttachment(moviesList);
    		imageLabelData.right = new FormAttachment(100);
    		imageLabel.setLayoutData(imageLabelData);
    
    		// Set image location
    		FormData descriptionTextData = new FormData();
    		descriptionTextData.top = new FormAttachment(imageLabel);
    		descriptionTextData.left = new FormAttachment(imageLabel, 0, SWT.LEFT);
    		descriptionTextData.right = new FormAttachment(100);
    		descriptionTextData.bottom = new FormAttachment(moviesList, 0, SWT.BOTTOM);
    		descriptionText.setLayoutData(descriptionTextData);
  6. Add commands to the screen. For more information on the Command class, see the class Command.

    		// Add commands
    		exitCommand = new Command(mainComposite, Command.EXIT, 0);
    		exitCommand.setText("Exit");
    		exitCommand.addSelectionListener(this);
    

    In the code above, a new Command instance is created for exiting. setText() defines the text to be displayed for the command. Finally, the command is associated with a typed listener called SelectionListener, which invokes the appropriate method after an event has been generated by the control. For more information on eSWT API typed listeners, see the package org.eclipse.swt.events.

    		reserveMovieCommand = new Command(mainComposite, Command.GENERAL, 1);
    		reserveMovieCommand.setText("Reserve");
    		reserveMovieCommand.addSelectionListener(this);
    		reserveMovieCommand.setDefaultCommand();
      }

    In the code above, a new Command instance is created for reserving a movie. setText() defines the text to be displayed for the command. Then, the command is associated with a typed listener (see the package org.eclipse.swt.events) called SelectionListener, which invokes the appropriate method after an event has been generated by the control. Finally, setDefaultCommand() is used to set the command as a default command, meaning that a device selection key can be used to activate the command. For more information on default commands, see the class Command.

  7. Add rules for events.

    	// Called when a command is invoked or a movie is selected
    	public void widgetSelected(SelectionEvent e) {
    		if (e.widget == exitCommand) {
    			destroy();
    			main.exit();
    		} else if (e.widget == reserveMovieCommand) {
    			if (moviesList.getSelectionIndex() >= 0) {
                    // Order of operations is important.
                    // We need to destroy before reserve that will create a new UI. 
                    // But need to call moviesList.getSelectionIndex() before destroy. 
    				Movie selectedMovie = (Movie)movies.elementAt(moviesList.getSelectionIndex());
    				destroy();
    				main.startReserve(selectedMovie);
    			}
    		} else if (e.widget == moviesList) {
    			// This is called when the user selects a movie
    			movieSelected(moviesList.getSelectionIndex());
    		}
    	}
    
    	public void widgetDefaultSelected(SelectionEvent e) {
    		// Do nothing
    	}
    
    	// Called when a movie is selected.
    	// Take the picture and name and put in the
    	// right side window. 
    	private void movieSelected(int index) {
    		if (index >= 0) {
    			Movie m = (Movie) movies.elementAt(index);
    			if (m != null) {
    				if (m.getImageLocation()!= null) {
    					try {
    						InputStream stream = getClass().getResourceAsStream(
    								m.getImageLocation());             
    						if (stream != null) {
    							Image newImage = new Image(mainComposite.getDisplay(), stream);
    							imageLabel.setImage(newImage);
    							if(image != null) {
    								image.dispose();
    							}
    							image = newImage;
    							stream.close();
    						}
    					} catch (IOException e) {
    						// Do nothing, a picture is missing
    					}
    					descriptionText.setText(m.getDescription());
    				}
    			}
    		}
    	}
    
    	private void destroy() {
    		// Dispose the widgets by disposing the parent
    		mainComposite.dispose();    
    		// Dispose the current image
    		if (image != null && !image.isDisposed()) {
    			image.dispose();
    		}
    	}
    
    }

ReservationForm

The ReservationForm class contains a typical form for entering data required for making a reservation. The basic fields contained include name, family name, and date. Note that the seating arrangement is obtained in a separate form. The form is created using a GridLayout with two columns that contain standard eSWT widgets. The chosen layout is as follows:

Figure 41: Ticket reservation screen

In the first column of the GridLayout are five Labels. In the second column are five other widgets: Two Text widgets, a DateEditor, Combo, and at the bottom a ConstrainedText widget. Organizing the layout for these widgets is described in step 5.

  1. Create the ReservationForm class file.

  2. Import the required classes and assign this class to the package moviebooking.ui.

    package moviebooking.ui;
    
    import java.util.Vector;
    import java.util.Calendar;
    import java.util.Date;
    import java.util.Hashtable;
    
    import moviebooking.moviedb.Movie;
    import moviebooking.moviedb.Showing;
    
    import org.eclipse.ercp.swt.mobile.Command;
    import org.eclipse.ercp.swt.mobile.ConstrainedText;
    import org.eclipse.ercp.swt.mobile.DateEditor;
    import org.eclipse.swt.SWT;
    import org.eclipse.swt.events.ModifyEvent;
    import org.eclipse.swt.events.ModifyListener;
    import org.eclipse.swt.events.SelectionEvent;
    import org.eclipse.swt.events.SelectionListener;
    import org.eclipse.swt.layout.GridData;
    import org.eclipse.swt.layout.GridLayout;
    import org.eclipse.swt.widgets.Composite;
    import org.eclipse.swt.widgets.Label;
    import org.eclipse.swt.widgets.Shell;
    import org.eclipse.swt.widgets.Text;
    import org.eclipse.swt.widgets.Combo;
    
    /**
     * This class contains a common Form to enter the data needed to make
     * a reservation. It contains fields such as first name,
     * family name date, etc.
     * 
     * The seating location is obtained in a separate form
     */
    public class ReservationForm implements SelectionListener, ModifyListener {
    
    	private MovieBooking main;
    	private Command exitCommand, seatCommand, confirmCommand;
    	private DateEditor movieDate;
    	private Combo movieTime;
    	private Composite mainComposite;
    	private Hashtable validDates = new Hashtable();  
    	private Text firstNameText, familyNameText;
    	private ConstrainedText ticketsText;
    	private int[][] selectedSeats;
  3. Build the Shell. For more information the Shell class, see the class Shell.

    	ReservationForm(MovieBooking main, Shell mainShell, Movie movie) {
    		this.main = main;
    		mainShell.setText("Reservation");
    
    		mainComposite = new Composite(mainShell, SWT.NONE);
    
    		// Size and location of this Composite are controlled by 
    		// the GridLayout of the mainShell
    		mainComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
  4. Create widgets. For a full list of widgets offered and additional information, see the package org.eclipse.swt.widgets .

    		// Create all the widgets in the Composite
    		Label firstNameLabel = new Label(mainComposite, SWT.NONE);
    		firstNameLabel.setText("First name");
    		// Contains the first name
    		firstNameText = new Text(mainComposite, SWT.SINGLE | SWT.BORDER);
    		firstNameText.setTextLimit(20);
    
    		Label familyNameLabel = new Label(mainComposite, SWT.NONE);
    		familyNameLabel.setText("Family name");
    		// Contains the family name
    		familyNameText = new Text(mainComposite, SWT.SINGLE | SWT.BORDER);
    		familyNameText.setTextLimit(20);
    
    		Label dateLabel = new Label(mainComposite, SWT.NONE);
    		dateLabel.setText("Date");
    		// Date for the movie, by default today
    		movieDate = new DateEditor(
    				mainComposite,
    				SWT.NONE,
    				DateEditor.DATE | SWT.BORDER);
    		movieDate.setDate(new Date());
    		movieDate.addModifyListener(this);
    
    		// A drop-down list of times
    		Label timeLabel = new Label(mainComposite, SWT.NONE);
    		timeLabel.setText("Time");
    		movieTime = new Combo(mainComposite, SWT.DROP_DOWN | SWT.READ_ONLY | SWT.BORDER);
    
    		Vector showings = movie.getShowings();
    		Vector times = new Vector();
    		// Store in the time combo the time valid
    		// for all showings, validDates contains 
    		// a map between showingDate and showing
    		for(int i = 0; i < showings.size(); ++i) {
    			Showing show = (Showing)showings.elementAt(i);
    			Calendar cal = Calendar.getInstance();
    			cal.setTime(show.getDate());
    			String showingTime = cal.get(Calendar.HOUR_OF_DAY) + ":00";
    			if (!times.contains(showingTime)) {
    				times.addElement(showingTime);
    			}
    			validDates.put(show.getDate(), show);
    		}
    		for(int i = 0; i < times.size(); ++i) {
    			movieTime.add((String) times.elementAt(i));
    		}
    		movieTime.select(0);
    		movieTime.addSelectionListener(this);
    
    		Label ticketsLabel = new Label(mainComposite, SWT.NONE);
    		ticketsLabel.setText("Tickets");
    
    		// Amount of tickets, note that ConstrainedText only takes numbers
    		ticketsText = new ConstrainedText(mainComposite, SWT.SINGLE | SWT.BORDER, ConstrainedText.NUMERIC);
    		ticketsText.addModifyListener(this);
    		ticketsText.setText("2");
    
  5. Organize the widgets by using a layout. For a full list of layouts offered and additional information, see the package org.eclipse.swt.layout.

    		// Use a grid layout with two columns
    		GridLayout layout = new GridLayout();
    		layout.makeColumnsEqualWidth = false;
    		layout.numColumns = 2;
    		mainComposite.setLayout(layout);
    
    		// Distribute the forms using the layout
    		firstNameText.setLayoutData(
    				new GridData(SWT.FILL, SWT.CENTER, true, false));
    		familyNameText.setLayoutData(
    				new GridData(SWT.FILL, SWT.CENTER, true, false));
    		ticketsText.setLayoutData(
    				new GridData(SWT.FILL, SWT.CENTER, true, false));
    		movieDate.setLayoutData(
    				new GridData(SWT.FILL, SWT.CENTER, true, false));
    		movieTime.setLayoutData(
    				new GridData(SWT.FILL, SWT.CENTER, true, false));
    
    		setCommands();
    		
    		mainShell.layout();
    	}
  6. Add commands. For more information on the Command class, see the class Command.

      private void setCommands() {
        // add commands
        exitCommand = new Command(mainComposite, Command.EXIT, 0);
        exitCommand.setText("Exit");
        exitCommand.addSelectionListener(this);
    
        seatCommand = new Command(mainComposite, Command.GENERAL, 1);
        seatCommand.setText("Seating");
        seatCommand.addSelectionListener(this);
        seatCommand.setDefaultCommand();
      }
  7. Add rules for events.

    	// Called when some command is selected
    	public void widgetSelected(SelectionEvent e) {
    		if (e.widget == exitCommand) {
    			// Exit the main application
    			destroy();
    			main.exit();
    		} else if (e.widget == movieTime) {
    			selectedSeats = null;
    			if (confirmCommand != null) {
    				confirmCommand.dispose();
    				confirmCommand = null;      
    			}
    		} else if (e.widget == seatCommand) {
    			// Iterate over the valid dates
    			Date showingDate = calculateTime();
    			
    			if (validDates.containsKey(showingDate) || (ticketsText.getText().length() == 0)) {        
    				int ticketsCount = 0;
    				try {
    					ticketsCount = Integer.parseInt(ticketsText.getText());
    				} catch (NumberFormatException ex) {
    					// Ignore, just use 0
    				}
    				if (ticketsCount == 0) {
    					return;
    				}
    				Showing show = (Showing) validDates.get(showingDate);
    				exitCommand.dispose();
    				exitCommand = null;
    				seatCommand.dispose();
    				seatCommand = null;
    				if (confirmCommand != null) {
    					confirmCommand.dispose();
    					confirmCommand = null;
    				}
    				main.showSeatingDialog(show, selectedSeats, ticketsCount);
    			} else {
    				main.displayMessageBox(SWT.ICON_ERROR | SWT.OK,
    						"Error",
    				"Wrong date selected");
    			}
    		} else if (e.widget == confirmCommand) {
    			Date showingDate = calculateTime();
    			// Validate the fields
    			if (selectedSeats == null || firstNameText.getText().equals("")
    					|| familyNameText.getText().equals("")) {        
    				main.displayMessageBox(SWT.ICON_ERROR | SWT.OK,
    						"Error",
    				"Missing required fields");
    				return;
    			} else {
    				// Request to main to make the reservation
    				String firstName = firstNameText.getText();
    				String familyName = familyNameText.getText();
    				destroy();
    				main.confirmReservation(firstName,
    						familyName, (Showing) validDates
    						.get(showingDate), selectedSeats);
    			}
    		}
    	}
    
    	public void widgetDefaultSelected(SelectionEvent e) {}
    
    	// Called when the user has selected the seats
    	void setSelectedSeats(int[][] selected) {
    		this.selectedSeats = selected;
    		setCommands();
    		if (confirmCommand == null && selectedSeats != null) {
    			// Add a new command      
    			confirmCommand = new Command(
    					mainComposite,
    					Command.GENERAL,
    					1);
    			confirmCommand.setText("Confirm");
    			confirmCommand.addSelectionListener(this);
    			confirmCommand.setDefaultCommand();
    		}
    	}
    
    	void cancelSeatSelection() {
    		selectedSeats = null;
    		if (confirmCommand != null) {
    			confirmCommand.dispose();
    			confirmCommand = null;      
    		}
    		setCommands();
    	}
    
    	// Close the main composite and dispose it
    	private void destroy() {
    		mainComposite.dispose();    
    	}
  8. Fetch the time and date of the movie showing.

    	// Create a time based on movieDate and timeCombo
    	private Date calculateTime() {
    		if (movieTime.getSelectionIndex() == -1) {
    			return null;
    		} else {
    			Calendar cal = Calendar.getInstance();
    			// Take the date from movieDate
    			Date showingDate = movieDate.getDate();  
    			cal.setTime(showingDate);
    
    			// Get the time from movieTime, so we remove the last ":00"
    			// Notice that we expect only exact minutes
    			String time = movieTime.getItem(movieTime.getSelectionIndex());
    			int hourOfDay = Integer.parseInt(time.substring(0, time.indexOf(':')));
    			int minutes = Integer.parseInt(time.substring(time.indexOf(':') + 1, time.length() - 1));
    
    			cal.set(Calendar.HOUR_OF_DAY, hourOfDay);
    			cal.set(Calendar.MINUTE, minutes);
    			cal.set(Calendar.SECOND, 0);
    			cal.set(Calendar.MILLISECOND, 0);
    			return cal.getTime();
    		}
    	}
    
    	public void modifyText(ModifyEvent e) {
    		selectedSeats = null;
    		if (confirmCommand != null) {
    			confirmCommand.dispose();
    			confirmCommand = null;      
    		}
    	}
    
    }