// Copyright 2001-2005, Danger, Inc.  All Rights Reserved.
// This file is subject to the Danger, Inc. Sample Code License,
// which is provided in the file SAMPLE_CODE_LICENSE.
// Copies are also available from http://developer.danger.com/

package com.example.GUIDemo;

import java.util.Vector;

import danger.app.Application;
import danger.app.Event;
import danger.audio.AudioException;
import danger.audio.AudioManager;
import danger.audio.Sequence;
import danger.helpers.AddToContactsHelper;
import danger.ui.AudioPreviewWindow;
import danger.ui.AudioRecordWindow;
import danger.ui.Bitmap;
import danger.ui.Button;
import danger.ui.ClockView;
import danger.ui.DialogWindow;
import danger.ui.FolderView;
import danger.ui.ImageWindow;
import danger.ui.ListContainer;
import danger.ui.Menu;
import danger.ui.MenuItem;
import danger.ui.MonthView;
import danger.ui.NumberField;
import danger.ui.PendingStatusView;
import danger.ui.ProgressWindow;
import danger.ui.RingTonePickerMenu;
import danger.ui.ScreenWindow;
import danger.ui.Shortcut;
import danger.ui.StaticText;
import danger.ui.StaticTextBox;
import danger.ui.Table;
import danger.ui.TableCell;
import danger.ui.TypeAheadTextField;
import danger.ui.View;
import danger.ui.geometry.Oval;
import danger.ui.geometry.Triangle;
import danger.ui.photopicker.PhotoPicker;
import danger.util.DEBUG;
import danger.util.Folder;

/**
 * Implements a main window class. It is created from a Screen resource. The
 * layout starts with the DemoFolderView, and the StaticText headers.
 * 
 * This ProgramWindow handles all the widgets and screens that cannot be created
 * via resources. These are created at runtime, in response to the menu commands
 * being sent.
 * 
 * This demonstrates how to use these program based views, and does not use any
 * resources so that it is more clear how the code applies.
 */
public class ProgramWindow extends ScreenWindow implements Resources, Commands {

	// Convenience constants to make the code more clear where used.
	private static final int kJustOneRecord = 1;

	private static final int kSpaceBetweenViews = 5;

	private static final boolean kDontAutoResize = false;

	private static final boolean kWaitToRecord = false;

	// Fixes a bug with 2.3 version of AddToContactsHelper.
	// Defined in 2.3 as the same constant.
	public static final int INCLUDE_IM = 128;

	/**
	 * Local reference for the application, to avoid the need for coercion.
	 */
	private static GUIDemo sApplication;

	/**
	 * Local reference to commonly used Strings. Made static to avoid constant
	 * reloading.
	 */
	private static String sDoneString;

	private static String sNextString;

	private static String sPreviousString;

	/**
	 * The main scrolling view demo items, DemoFolderView, with containing
	 * folders, then all the demo items in the folder.
	 */
	private DemoFolderView mFolderView;

	// ------------------------------------------------------------------------
	/**
	 * This is called after the window has been created from resources. Since
	 * the FolderView cannot be created from resources, it is built here after
	 * the Screen has loaded. The resource view is used only to get dimensions.
	 * 
	 * The menu associated with the Screen has the structure for the FolderView
	 * built into it, so it is decoded, and each item is added to the
	 * FolderView.
	 * 
	 * Anything missing a submenu is presumed to not be part of the structure,
	 * and will be skipped, like dividers and other menu items.
	 */
	public void onDecoded() {
		// Get a coerced reference to the current App for later use.
		sApplication = (GUIDemo) Application.getCurrentApp();
		sDoneString = sApplication.getString(ID_DONE);
		sNextString = sApplication.getString(ID_NEXT);
		sPreviousString = sApplication.getString(ID_PREVIOUS);

		// Find the ScrollView descendant that the Table will be stuffed into.
		// It also must be the main focus, so that it will scroll when needed.
		View view = getDescendantWithID(ID_FOLDER_VIEW);

		mFolderView = new DemoFolderView(view.getFrame());
		mFolderView.setHasScrollbar(true);
		mFolderView.show();

		addChild(mFolderView);
		setFocusedChild(mFolderView);

		// The desired hierarchy for the FolderView is found in the Menu
		// resource for this ScreenWindow. Drive the Menu structure, and plug
		// all the strings into Folders, and folderItems. The main menu is
		// the name of the Folder, the submenu has the item names and events.
		Folder folder;
		MenuItem item;
		Menu sub;
		Menu m = getActionMenu();

		for (int i = 0; i < m.itemCount(); i++) {
			item = m.getItem(i);
			if (item.hasSubmenu()) {
				folder = mFolderView.createFolder(item.getTitle());

				sub = item.getSubMenu();
				sub.collateMenuItems();
				for (int j = 0; j < sub.itemCount(); j++) {
					folder.addItem(sub.getItem(j));
					if (sub.getItem(j).getEvent() == null) {
						sub.getItem(j).disable();
					}
				}
			}
		}

		super.onDecoded();
	}

	/**
	 * Handles device buttons, specifically the Jump, Cancel, and Done buttons.
	 */
	public boolean eventWidgetUp(int inWhichWidget, Event inEvent) {

		switch (inWhichWidget) {
		// For both the Cancel or Back/Done button from the ProgramWindow, it
		// will jump back to the launcher screen to provide a better
		// experience.
		case Event.DEVICE_BUTTON_BACK:
			sApplication.returnToLauncher();
			return true;

		case Event.DEVICE_BUTTON_CANCEL:
			sApplication.returnToLauncher();
			return true;

		case Event.DEVICE_BUTTON_LEFT_SHOULDER:
			mFolderView.previousSelection();
			return true;

		case Event.DEVICE_BUTTON_RIGHT_SHOULDER:
			mFolderView.nextSelection();
			return true;

		case Event.DEVICE_ARROW_LEFT:
			sApplication.showResWindow();
			return true;
		}

		// Always make sure to call super.eventWidgetUp for other events so the
		// system can execute the correct behavior (e.g. returning to the Jump
		// screen with phone selected).
		return (super.eventWidgetUp(inWhichWidget, inEvent));
	}

	/**
	 * Handles events. Called automatically whenever this window receives an
	 * event. The only events handled are related to the menu. All other events
	 * are handled automatically by the window, by calling super.
	 */
	public boolean receiveEvent(Event e) {
		DialogWindow dw;
		String[] viewStrings;

		switch (e.type) {

		// Sent from a Done choice in a screen menu. Just hide the top
		// window to display this ProgramWindow. Hide is superior to
		// showing this(), because the hidden window can then be garbage
		// collected.
		case EVENT_HIDE:
			sApplication.getFrontWindow().hide();
			return true;

		case EVENT_SHOW_RESOURCEBASED:
			sApplication.showResWindow();
			return true;

		case EVENT_SHOW_PROGRAMMATIC:
			show();
			return true;

		case EVENT_PREVIOUS:
			// Hide any prior screens that might be open.
			if (sApplication.getFrontWindow() != this) {
				sApplication.getFrontWindow().hide();
			}
			mFolderView.previousSelection();
			return true;

		case EVENT_NEXT:
			// Hide any prior screens that might be open.
			if (sApplication.getFrontWindow() != this) {
				sApplication.getFrontWindow().hide();
			}
			mFolderView.nextSelection();
			return true;

		/** *************** Controls **************************** */

		case EVENT_RING_TONE_PICKER_MENU:
			viewStrings = sApplication
					.getStringArray(ID_RINGTONEPICKERMENU_STRINGS);

			Event event = new Event(this, EVENT_HIDE);
			RingTonePickerMenu ringToneMenu = new RingTonePickerMenu(event);

			// Text description of how to display this item.
			StaticText statictext = new StaticText(viewStrings[1]);
			statictext.setLeft(5);
			statictext.setTop(15);
			statictext.show();

			// Create a ScreenWindow in order to attach the menu to it.
			ScreenWindow screenWindow = new ScreenWindow();

			// Using a subtitle, to keep Programmatic at the top.
			screenWindow.setTitle(sApplication.getString(ID_PROGRAMMATIC));
			screenWindow.setSubTitle(viewStrings[0]);
			screenWindow.addChild(statictext);

			// Set up the ActionMenu to be the RingTonePickerMenu
			Menu menu = screenWindow.getActionMenu();
			MenuItem item = menu.addItem(viewStrings[2]);
			item.addSubMenu(ringToneMenu);
			menu.addDivider();

			// Add in the navigation menu items, for Done, L/R Shoulders.
			menu.addFromResource(sApplication.getResources(), ID_SCREENS_MENU,
					this);

			screenWindow.show();
			return true;

		// The ListSelectMenu is closely incorporated with the
		// TypeAheadTextField. See the resource version for comparison.
		// A possible direct use of ListSelectMenu would be to create a
		// select menu as an override for different drawing or layout.
		// The menu is what pops up in response to the TypeAheadTextField.
		case EVENT_LIST_SELECT_MENU:
			viewStrings = sApplication.getStringArray(ID_LISTSELECT_STRINGS);

			StaticText st = new StaticText(viewStrings[1]);
			st.show();

			TypeAheadTextField typeAhead = new TypeAheadTextField(
					new FruitFinder());
			typeAhead.setSize(90, 15);
			typeAhead.moveBy(0, 15);
			typeAhead.show();
			
			dw = makeDialog(viewStrings[0]);
			dw.addChild(st);
			dw.addChild(typeAhead);
			dw.setFocusedChild(typeAhead);
			dw.show();
			return true;

		// In order to create a Table, it is necessary to use a Vector,
		// which in turn must be filled with TableCell elements. Other
		// objects cannot be used because they cause class cast errors.
		case EVENT_TABLE:
			viewStrings = sApplication.getStringArray(ID_TABLE_STRINGS);

			// The Vector is used for each row being added to the table.
			Vector vector;
			// The TableCell is used for each column in a row.
			TableCell aCell;

			Table table = new Table();
			table.setBorderWidth(1); // makes grid visible.
			table.setAcceptFocus(false); // nothing to select.

			for (int i = 0; i < 3; i++) {
				vector = new Vector(3); // 3 columns per item.
				for (int j = 0; j < 3; j++) {
					aCell = new TableCell(table);
					aCell.setElementToString(viewStrings[i * 3 + j + 1]);
					aCell.setSize(70, 11);
					vector.addElement(aCell);
				}
				table.addRow(vector);
			}
			table.autoSize();
			table.show();

			dw = makeDialog(viewStrings[0]);
			dw.addChild(table);
			dw.show();
			return true;

		/** *************** Dialogs ***************************** */
		case EVENT_ADD_TO_CONTACTS_HELPER:
			viewStrings = sApplication
					.getStringArray(ID_ADDTOCONTACTSHELPER_STRINGS);

			// This fixes an unusual bug. Under 2.0 it is safe to use the
			// INCLUDE_ALL parameter. Under 2.3 this will crash, because of the
			// addition of the IM field. To avoid having more than 6 items in
			// the window, this subtracts off one, the IM. This code
			// will thus work on both 2.0 and 2.3, but loses the IM. The
			// IM field has a secondary crash, which is why it is being
			// excluded, rather than say MIDDLENAME. The constant is defined
			// locally, but matches the 2.3 version.
			int includes = AddToContactsHelper.INCLUDE_ALL - INCLUDE_IM;

			AddToContactsHelper helper = AddToContactsHelper.newContactHelper(
					includes, kJustOneRecord);

			// Set the title to the class name, and removed the cancel so it
			// can be fully seen. This is not normally necessary.
			helper.setTitle(viewStrings[0]);

			// Disable the Cancel button for this demo. The setFrameShortcut is
			// necessary to avoid a null pointer crash.
			helper.setShowCancel(false);
			helper.setFrameButtonShortcut(DialogWindow.TOP_POSITION1,
					Shortcut.NONE);

			helper.show();

			// The following business is not necessary, and is here merely to
			// support the L/R shoulder button navigation. Normally this is
			// being handled through subclassing, but this class cannot be
			// subclassed because of the static creation.
			Button button;
			button = helper.addBottomFrameButton(sNextString, EVENT_NEXT, 0,
					null, Shortcut.RIGHT_SHOULDER_BUTTON);
			button.setSize(0, 0);
			button.setAcceptFocus(false);
			button.setEventListener(this);
			button = helper.addBottomFrameButton(sPreviousString,
					EVENT_PREVIOUS, 0, null, Shortcut.LEFT_SHOULDER_BUTTON);
			button.setSize(0, 0);
			button.setAcceptFocus(false);
			button.setEventListener(this);
			return true;

		// This will not work correctly in the simulator, because sound
		// is not fully supported. Works correctly upon the device.
		case EVENT_AUDIO_PREVIEW_WINDOW:
			viewStrings = sApplication
					.getStringArray(ID_AUDIOPREVIEWWINDOW_STRINGS);

			// Fetch the song from the assets, using the AudioManager.
			// The song or any other audio is passed to the AudioPreviewWindow.
			Sequence music = null;
			try {
				music = AudioManager.createSequence(); // may throw exception.
				
				music.loadFromResource(ID_MUSIC);
				AudioPreviewWindow wind = AudioPreviewWindow.play(
						viewStrings[0], music.getData());
				wind.setTitle(viewStrings[0]);

				// Hack in a couple of invisible buttons to allow navigation
				// with L/R shoulder buttons. Setting the listener is also
				// necessary so that these button events go to the
				// ProgramWindow.
				// This button hack is necessary because these windows cannot
				// be subclassed because of the static constructor.
				wind.setListener(this);
				button = wind.addBottomFrameButton(sNextString, EVENT_NEXT, 0,
						null, Shortcut.RIGHT_SHOULDER_BUTTON);
				button = wind.addBottomFrameButton(sPreviousString,
						EVENT_PREVIOUS, 0, null, Shortcut.LEFT_SHOULDER_BUTTON);
			} catch (AudioException e1) {
				DEBUG.p("Could not load Audio file for AudioPreviewWindow.");
			}
			return true;

		// This will crash in the simulator, because sound is not fully
		// supported. Works correctly upon the device for both 2.0 and 2.3.
		// To get the resulting audio, call getRingToneObject() when handling
		// the Done event.
		case EVENT_AUDIO_RECORD_WINDOW:
			viewStrings = sApplication
					.getStringArray(ID_AUDIORECORDWINDOW_STRINGS);

			AudioRecordWindow audioWindow = AudioRecordWindow.create(new Event(
					EVENT_HIDE), new Event(EVENT_HIDE), kWaitToRecord);
			audioWindow.setTitle(viewStrings[0]);
			audioWindow.setHideOnButtonEvent(true); // hide upon cancel or done.
			audioWindow.show();

			// Hack in a couple of invisible buttons to allow navigation with
			// L/R shoulder buttons. Setting the listener is also necessary
			// so that these button events go to the ProgramWindow.
			// This button hack is necessary because these windows cannot
			// be subclassed because of the static constructor.
			audioWindow.setListener(this);
			button = audioWindow.addBottomFrameButton(sNextString, EVENT_NEXT,
					0, null, Shortcut.RIGHT_SHOULDER_BUTTON);
			button = audioWindow.addBottomFrameButton(sPreviousString,
					EVENT_PREVIOUS, 0, null, Shortcut.LEFT_SHOULDER_BUTTON);
			return true;

		case EVENT_DATE_DIALOG:
			viewStrings = sApplication.getStringArray(ID_DATEDIALOG_STRINGS);

			LRDateDialog date = new LRDateDialog(viewStrings[0], this, 0);

			// Disable the Cancel button for this demo. The setShortcut fixes
			// a resulting null pointer crash.
			date.setShowCancel(false);
			date.setFrameButtonShortcut(DialogWindow.TOP_POSITION1,
					Shortcut.NONE);

			date.show();
			return true;

		case EVENT_PROGRESS_WINDOW:
			viewStrings = sApplication
					.getStringArray(ID_PROGRESSWINDOW_STRINGS);

			ProgressWindow dialog = new ProgressWindow(viewStrings[0]);
			dialog.setProgressText(viewStrings[1]);

			// Disable the Cancel button for this demo. The setShortcut fixes
			// a resulting null pointer crash.
			dialog.setShowCancel(false);
			dialog.setFrameButtonShortcut(DialogWindow.TOP_POSITION1,
					Shortcut.NONE);

			// The default ProgressWindow will have a Cancel button. This will
			// remove that, and make a default Done button.
			button = dialog.addBottomFrameButton(sDoneString, 0, 0, null,
					Shortcut.BACK_BUTTON);
			dialog.setDefaultFrameButton(button);

			// There is a bit of trouble here under 2.0. The ProgressWindow
			// is marked 'final', and hence cannot be subclassed as
			// LRProgressWindow. Thus, to provide the L/R shoulder
			// functionality, buttons need to be added to the window. Under 2.0
			// the Previous button + shortcut does not draw properly.
			// To avoid the problem altogether, this is going to hack in two
			// 0 sized buttons, so that they don't show. And, make them not
			// accept focus. So, they will invisibly support the L/R function.
			// Clearly this is not a recommended approach. Under 2.3, this
			// still works.
			button = dialog.addBottomFrameButton(sNextString, EVENT_NEXT, 0,
					null, Shortcut.RIGHT_SHOULDER_BUTTON);
			button.setSize(0, 0);
			button.setAcceptFocus(false);
			button = dialog.addBottomFrameButton(sPreviousString,
					EVENT_PREVIOUS, 0, null, Shortcut.LEFT_SHOULDER_BUTTON);
			button.setSize(0, 0);
			button.setAcceptFocus(false);

			// The listener must be set to this(), so that the L/R events make
			// it here to be handled.
			dialog.setListener(this);

			dialog.show();
			return true;

		case EVENT_TOOL_TIP_WINDOW:
			Version.showToolTipWindow();
			return true;

		/** *************** Text ******************************** */
		case EVENT_LINK_TEXT_BOX:
			Version.showLinkTextBox();
			return true;

		case EVENT_LOG_TEXT_BOX:
			Version.showLogTextBox();
			return true;

		// The mutable text box is similar to the SelectTextBox, but is backed
		// by a changeable buffer, so it is suited for larger blocks of text.
		// It is used by the LogTextBox for log functionality.
		case EVENT_MUTABLE_TEXT_BOX:
			Version.showMutableTextBox();
			return true;

		case EVENT_NUMBER_FIELD:
			viewStrings = sApplication.getStringArray(ID_NUMBERFIELD_STRINGS);

			NumberField numbers = new NumberField(viewStrings[1]);
			numbers.setWidth(100);
			numbers.show();

			dw = makeDialog(viewStrings[0]);
			dw.addChild(numbers);
			dw.setFocusedChild(numbers);
			dw.show();
			return true;

		case EVENT_PHONE_NUMBER_TEXT_FIELD:
			Version.showPhoneNumberTextField();
			return true;

		case EVENT_SCROLLING_TEXT_BOX:
			Version.showScrollingTextBox();
			return true;

		case EVENT_SELECT_TEXT_BOX:
			Version.showSelectTextBox();
			return true;

		/** *************** Views ******************************* */

		case EVENT_CLOCK_VIEW:
			viewStrings = sApplication.getStringArray(ID_CLOCKVIEW_STRINGS);

			ClockView clockView = new ClockView();
			clockView.updateTime();
			clockView.show();

			dw = makeDialog(viewStrings[0]);
			dw.addChild(clockView);
			dw.show();
			return true;

		case EVENT_LIST_CONTAINER:
			viewStrings = sApplication.getStringArray(ID_LISTCONTAINER_STRINGS);

			ListContainer list = new ListContainer(kSpaceBetweenViews,
					kDontAutoResize);
			list.setActualSize(50, 50);
			list.show();

			// Add a series of simple shape views to the list container.
			for (int i = 0; i < 7; i++) {
				Oval oval = new Oval();
				list.addItem(oval);

				// Size set must be done after addItem(), otherwise
				// ListContainer will modify the width. 
				oval.setSize(25 + i * 2, 25);
				oval.setFillColor(0x0033ff33);
				oval.setFilled(true);
				oval.show();
			}

			dw = makeDialog(viewStrings[0]);
			dw.addChild(list);
			dw.show();
			return true;

		case EVENT_FOLDER_VIEW:
			viewStrings = sApplication.getStringArray(ID_FOLDERVIEW_STRINGS);

			FolderView folderView = new FolderView(15, 15, 150, 60);
			Folder f = folderView.createFolder(viewStrings[1]);
			f.addItem(viewStrings[2]);
			folderView.createFolder(viewStrings[3]);
			folderView.createFolder(viewStrings[4]);
			folderView.show();

			dw = makeDialog(viewStrings[0]);
			dw.addChild(folderView);
			dw.setFocusedChild(folderView);
			dw.show();
			return true;

		case EVENT_MULTI_FOLDER_VIEW:
			Version.showMultiFolderView();
			return true;

		case EVENT_MONTH_VIEW:
			viewStrings = sApplication.getStringArray(ID_MONTHVIEW_STRINGS);

			MonthView mv = new MonthView(8, 1945);
			mv.setTitle(viewStrings[1]);
			mv.setSize(100, 60);
			mv.show();
			// The MonthView can handle being selected and clicked. When that
			// happens, the event specified here will be sent. In this case,
			// it is sent to this owning window, which will just hide().
			// Either this event must be specified, or the MonthView set to not
			// accept focus, or a null object crash will occur.
			mv.setEvent(new Event(this, EVENT_HIDE));

			dw = makeDialog(viewStrings[0]);
			dw.addChild(mv);
			dw.show();
			return true;

		// If there are no pending status items, the view is empty, and the
		// check of bitmapCount will activate the text message.
		case EVENT_PENDING_STATUS_VIEW:
			viewStrings = sApplication
					.getStringArray(ID_PENDINGSTATUSVIEW_STRINGS);
			StaticText text = new StaticText();
			text.setTop(25);
			text.show();

			PendingStatusView pendingStatus = new PendingStatusView(0,
					PendingStatusView.LAYOUT_HORIZONTAL, null);
			pendingStatus.setSize(50, 20);
			pendingStatus.show();
			if (pendingStatus.bitmapCount() == 0) {
				text.setText(viewStrings[1], StaticText.RESIZE_TO_FIT);
			}

			dw = makeDialog(viewStrings[0]);
			dw.addChild(pendingStatus);
			dw.addChild(text);
			dw.show();
			return true;

		case EVENT_TRIANGLE:
			viewStrings = sApplication.getStringArray(ID_TRIANGLE_STRINGS);

			Triangle tv = new Triangle(15, 5, 15, 25, 70, 45);
			// The setSize call is to fix a bug with Triangles. Left to its own
			// sizing, it will clip the triangle at the bottom right.
			tv.setSize(75, 50);
			tv.show();

			dw = makeDialog(viewStrings[0]);
			dw.addChild(tv);
			dw.show();
			return true;

		/** *************** Windows ***************************** */
		// This class is used in the Contacts app, to manage the categories.
		// It does not work very well, and is waiting for bug fixes to be
		// enabled for the demo. Best to not use this unless it does
		// exactly what is desired.
		// New and Discard do not do anything, requiring further code to
		// fully implement the class.
		// case EVENT_FOLDER_EDITOR:
		// viewStrings = sApplication.getStringArray(ID_FOLDEREDITOR_STRINGS);
		// These constructor strings are used to set up the menu.
		// For this example, the names are expected to be Categories,
		// like Friends, Family, Work, etc.
		// LRFolderEditor folderEditor = new LRFolderEditor(viewStrings[1],
		// viewStrings[2], null);
		// Using a subtitle, to keep Programmatic at the top.
		// folderEditor.setTitle(sApplication.getString(ID_PROGRAMMATIC));
		// folderEditor.setSubTitle(viewStrings[0]);
		// The CustomList is a required field, but not part of the
		// constructor. If unassigned, the class will crash.
		// This list is for the actual items to be edited.
		// ListView listView = new ListView();
		// listView.addItem(viewStrings[3]);
		// listView.addItem(viewStrings[4]);
		// listView.addItem(viewStrings[5]);
		// folderEditor.setCustomList(listView);
		// listView.autoResize(true); // otherwise it won't draw.
		// The HeaderView is required, but not part of the constructor.
		// If null, the rest of the view will not be drawn.
		// StaticText view = new StaticText(viewStrings[1]);
		// view.setBackgroundColor(Color.CYAN);
		// view.setFont(Font.findBoldSystemFont());
		// folderEditor.setHeaderView(view);
		// The static list is also required, but not part of the
		// constructor.
		// It is not edited via the menus. It must exist to allow the
		// views to draw, but can be empty.
		// listView = new ListView();
		// listView.autoResize(true);
		// listView.addItem(viewStrings[6]);
		// listView.addItem(viewStrings[7]);
		// folderEditor.setStaticList(listView);
		// Menu lrMenu;
		// Adding in the navigation Action menu, for L/R support.
		// lrMenu = folderEditor.getActionMenu();
		// lrMenu.addDivider();
		// lrMenu.addFromResource(sApplication.getResources(),
		// ID_SCREENS_MENU, this);
		//
		// folderEditor.show();
		// return true;
		case EVENT_MENU_WINDOW:
			viewStrings = sApplication.getStringArray(ID_MENUWINDOW_STRINGS);

			// Create a ScreenWindow in order to attach the menu to it.
			ScreenWindow screen = new ScreenWindow();

			// Using a subtitle, to keep Programmatic at the top.
			screen.setTitle(sApplication.getString(ID_PROGRAMMATIC));
			screen.setSubTitle(viewStrings[0]);

			// Set up the ActionMenu to be the Done L/R menu.
			Menu drawMenu = screen.getActionMenu();
			drawMenu.addFromResource(sApplication.getResources(),
					ID_SCREENS_MENU, this);

			// Now the key part, set the Class for the attached MenuWindow to
			// be the local sub-class DrawMenuWindow, so that specialized
			// drawing can be done.
			drawMenu.setMenuWindowClass(DrawMenuWindow.class);

			// Add a descriptive text item to the window.
			StaticTextBox helpText = new StaticTextBox();
			helpText.setText(viewStrings[1]);
			helpText.setPosition(15, 15);
			helpText.setSize(180, 50);
			helpText.setHasBorder(false);
			helpText.show();

			screen.addChild(helpText);
			screen.show();
			return true;

		case EVENT_IMAGE_WINDOW:
			viewStrings = sApplication.getStringArray(ID_IMAGEWINDOW_STRINGS);

			Bitmap bits = sApplication.getBitmap(ID_HANS);
			ImageWindow window = new ImageWindow(bits);

			// Using a subtitle, to keep Programmatic at the top.
			window.setTitle(sApplication.getString(ID_PROGRAMMATIC));
			window.setSubTitle(viewStrings[0]);

			// Adding in the navigation Action menu, for L/R support.
			Menu screenMenu;
			screenMenu = window.getActionMenu();
			screenMenu.addFromResource(sApplication.getResources(),
					ID_SCREENS_MENU, this);

			window.show();
			return true;

		case EVENT_PHOTO_PICKER:
			viewStrings = sApplication.getStringArray(ID_PHOTOPICKER_STRINGS);

			PhotoPicker picker = Version.showPhotoPicker();

			picker.setMaxSelectionCount(1);

			// The class name is so long that it draws over the top of
			// the hint text on the right. So, for this class only, it
			// has been squeezed down.
			picker.setTitle(viewStrings[0]);

			// Unfortunately, for this class, it is not possible to add
			// the L/R navigation items. The menu cannot be added to
			// the PhotoPicker window, because it is rebuilt completely
			// by adjustActionMenuState.
			// It also doesn't necessarily make sense to override the
			// shoulder buttons, because they are already used by the
			// PhotoPicker itself for taking pictures, and display.
			// screenMenu = picker.getActionMenu();
			// screenMenu.addFromResource(sApplication.getResources(),
			// ID_SCREENS_MENU, this);

			picker.show();
			return true;
		}

		return super.receiveEvent(e);
	}

	private static final boolean kNoCancel = false;

	/**
	 * Common routine to build a display dialog from scratch. The dialogs
	 * created will autosize to fit the various views that are added to the
	 * dialog.
	 * 
	 * This will also add a default Done button to the dialog, to keep it
	 * consistent. If the view should focus somewhere else, the caller can fix
	 * the initialFocus after this is created.
	 * 
	 * The LRDialogWindow is created, to handle the shoulder buttons for
	 * navigating forward and back through the list of available views.
	 */
	private DialogWindow makeDialog(String title) {
		DialogWindow dialog;
		Button done;

		dialog = new LRDialogWindow(title, DialogWindow.APP_DIALOG_STYLE,
				kNoCancel);
		done = dialog.addBottomFrameButton(sDoneString, 0, 0, null,
				Shortcut.BACK_BUTTON);
		dialog.setDefaultFrameButton(done);

		return dialog;
	}
}
