/** 
	 * VncCanvasActivity.java
	 * Archivo original: http://code.google.com/p/android-vnc-viewer/source/browse/branches/antlersoft/androidVNC/src/android/androidVNC/VncCanvasActivity.java
	 * Release: 39
	 * Modificado por: Alberto Población Navarro
	 * Añadido
	 *	- Declaracion de Sensores sensor;
	 *	- Añadido case R.id.itemInputFullMouseControl
	 *	- Añadido case R.id.itemScreenshot 
	 *  - Añadido case R.id.itemacelerometro  
	 *  - Añadido case R.id.itemorientacion
	 *  - Añadido case R.id.itembrujula
	 *  - Añadido case R.id.itemZoom
	 *  - Añadido case R.id.itemPegar
	 * Modificaciones: 	  
	 *  - Añadido declaracion Sensores 72
	 *  - Modificada private static final int inputModeIds[] = { R.id.itemInputFitToScreen, R.id.itemInputMouse, R.id.itemInputPan, R.id.itemInputTouchPanTrackballMouse, R.id.itemInputFullMouseControl};
	 
 */

/* 
 * This is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this software; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
 * USA.
 */

//
// CanvasView is the Activity for showing VNC Desktop.
//
package android.androidVNC;

import android.app.Activity;
import android.app.Dialog;
import android.content.ContentValues;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.DialogInterface.OnDismissListener;
import android.content.res.Configuration;
import android.graphics.Matrix;
import android.os.Bundle;
import android.util.Log;
import android.view.Display;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.Window;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ImageView.ScaleType;

public class VncCanvasActivity extends Activity{

	private final static String TAG = "VncCanvasActivity";

	AbstractInputHandler inputHandler;

	VncCanvas vncCanvas;
	
	VncDatabase database;
	//Flag para no mostrar le mensaje de ejecucion en segundo plano
	//en caso de que estemos cerrando la conexion
	Boolean disconnect=false; 
	
	private Sensores sensor;
	private Notificacion notificacion;
	private AndroidLog log;
	private MenuItem[] inputModeMenuItems;
	private AbstractInputHandler inputModeHandlers[];
	private ConnectionBean connection;
	private boolean trackballButtonDown;
	private static final int inputModeIds[] = { R.id.itemInputFitToScreen, R.id.itemInputMouse, R.id.itemInputPan, R.id.itemInputTouchPanTrackballMouse, R.id.itemInputFullMouseControl};

	
	@Override
	public void onCreate(Bundle icicle) {

		super.onCreate(icicle);
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		//Eliminamos el full screen para poder aprovexar las notificaciones
		//getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
		database = new VncDatabase(this);
		
		notificacion = Notificacion.Instancia(this);
		notificacion.notificar(getString(R.string.app_name),getString(R.string.running));
		log = AndroidLog.Instancia();
		
		Bundle extras = getIntent().getExtras();
		
		connection=new ConnectionBean();
		connection.Gen_populate((ContentValues)extras.getParcelable(VncConstants.CONNECTION));
		if (connection.getPort() == 0)
			connection.setPort(5900);
		
		// Parse a HOST:PORT entry
		String host=connection.getAddress();
		if (host.indexOf(':') > -1) {
			String p = host.substring(host.indexOf(':') + 1);
			try {
				connection.setPort( Integer.parseInt(p) );
			} catch (Exception e) {
			}
			connection.setAddress( host.substring(0, host.indexOf(':')));
		}
		
		vncCanvas = new VncCanvas(this, connection, new Runnable() {
			public void run() {
				setModes();
			}
		});
		
		setContentView(vncCanvas);
		
		inputHandler=getInputHandlerById(R.id.itemInputFitToScreen);
	}
	
	/**
	 * Set modes on start to match what is specified in the ConnectionBean; color mode (already done)
	 * scaling, input mode
	 */
	void setModes() {
		// If scale mode is not default
		if ( connection.getScaleMode() != ScaleType.FIT_CENTER)
		{
			//vncCanvas.setScaleType(connection.getScaleMode());
			//vncCanvas.scrollTo(-vncCanvas.getCenteredXOffset(),-vncCanvas.getCenteredYOffset());
			vncCanvas.setScaleType(ScaleType.MATRIX);
			connection.setScaleMode(ScaleType.MATRIX);
			AbstractInputHandler input = getInputHandlerByName(connection.getInputMode());
			if (input != null) {
				inputHandler = input;
			}
			updateInputMenu();
		}
	}
	
	/**
	 * Make sure mouse is visible on displayable part of screen
	 */
	void panToMouse()
	{
		if (! connection.getFollowMouse() || isFitToScreen())
			return;
		int x = vncCanvas.mouseX;
		int y = vncCanvas.mouseY;
		boolean panned = false;
		Display display = getWindowManager().getDefaultDisplay();
		int w = display.getWidth();
		int h = display.getHeight();
		AbstractBitmapData bitmapData = vncCanvas.bitmapData;
		
		int newX = absoluteXPosition;
		int newY = absoluteYPosition;
		
		if (x - newX >= w)
		{
			newX = x - w/2;
			if (newX + w > bitmapData.framebufferwidth)
				newX = bitmapData.framebufferwidth - w;
		}
		else if (x < newX)
		{
			newX = x - w/2;
			if (newX < 0)
				newX = 0;
		}
		if ( newX != absoluteXPosition ) {
			newX = newX - absoluteXPosition;
			absoluteXPosition += newX;
			panned = true;
		} else {
			newX = 0;
		}
		if (y - newY >= h)
		{
			newY = y - h/2;
			if (newY + h > bitmapData.framebufferheight)
				newY = bitmapData.framebufferheight - h;
		}
		else if (y < newY)
		{
			newY = y - h/2;
			if (newY < 0)
				newY = 0;
		}
		if ( newY != absoluteYPosition ) {
			newY = newY - absoluteYPosition;;
			absoluteYPosition += newY;
			panned = true;
		}
		else {
			newY = 0;
		}
		if (panned)
		{
			vncCanvas.scrollBy(newX, newY);
		}
	}
	
	@Override 
    public void onConfigurationChanged(Configuration newConfig) { 
		// ignore orientation/keyboard change 
	  Log.d(TAG, "ANTES DE CAMBIAR");
	  Log.d(TAG, "Absx: "+absoluteXPosition+" Widt: "+vncCanvas.getWidth()+" ImageWid: "+vncCanvas.getImageWidth());
      super.onConfigurationChanged(newConfig);

      Display display=getWindowManager().getDefaultDisplay();
      Log.d(TAG, "Display: +"+display.getWidth());
      if(absoluteXPosition+ display.getWidth()>=vncCanvas.getImageWidth()){
    	  absoluteXPosition=vncCanvas.getImageWidth()-(display.getWidth());
    	  vncCanvas.scrollTo(0, vncCanvas.getScrollY());
    	  vncCanvas.scrollTo(absoluteXPosition,vncCanvas.getScrollY());
      }
      if(absoluteYPosition+ display.getHeight()+vncCanvas.getTop()>=vncCanvas.getImageHeight()){
    	  absoluteYPosition=vncCanvas.getImageHeight()-(display.getHeight());
    	  vncCanvas.scrollTo(vncCanvas.getScrollX(),0);
    	  vncCanvas.scrollTo(vncCanvas.getScrollX(), absoluteYPosition);
      }
      Log.d(TAG, "Orientación: "+newConfig.orientation);

      
	}

	@Override 
    protected void onStop() { 
      vncCanvas.disableRepaints();
      if(!disconnect){
      notificacion.notificar("VNC Viewer sigue ejecutandose en segundo plano");
      log.log(TAG, "VNC Viewer sigue ejecutandose en segundo plano");
      }
      super.onStop();       
    } 

	@Override 
    protected void onRestart() {
      vncCanvas.enableRepaints();
      notificacion.notificar(getString(R.string.app_name) , "VNC Viewer se ejecuta en primer plano");
      log.log(TAG, "VNC Viewer se ejecuta en primer plano");
      super.onRestart();       
    } 
	
	Menu mi_menu;
	/** {@inheritDoc} */
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		mi_menu=menu;
		getMenuInflater().inflate(R.menu.vnccanvasactivitymenu, menu);
		
		menu.findItem(isFitToScreen() ? R.id.itemFitToScreen : R.id.itemOneToOne).setChecked(true);
			
		
		Menu inputMenu = menu.findItem( R.id.itemInputMode).getSubMenu();
		
		inputModeMenuItems = new MenuItem[inputModeIds.length];
		for ( int i=0; i<inputModeIds.length; i++)
		{
			inputModeMenuItems[i]=inputMenu.findItem(inputModeIds[i]);
		}
		updateInputMenu();
		menu.findItem(R.id.itemFollowMouse).setChecked(connection.getFollowMouse());
		return true;
	}
	
	/**
	 * Change the input mode sub-menu to reflect change in scaling
	 */
	void updateInputMenu()
	{
		if ( inputModeMenuItems == null ) {
			return;
		}
		if ( isFitToScreen())
		{
			for ( MenuItem item : inputModeMenuItems)
			{
				item.setEnabled(false);
				if ( item.getItemId()==R.id.itemInputFitToScreen)
					item.setChecked(true);
			}
		}
		else
		{
			for ( MenuItem item : inputModeMenuItems)
			{
				int id=item.getItemId();
				item.setEnabled( id!=R.id.itemInputFitToScreen);
				if (inputHandler == getInputHandlerById(id))
				{
					item.setChecked(true);
				}
			}
		}
	}
	
	AbstractInputHandler getInputHandlerById( int id)
	{
		if ( inputModeHandlers==null)
		{
			inputModeHandlers=new AbstractInputHandler[inputModeIds.length];
		}
		for ( int i=0; i<inputModeIds.length; ++i)
		{
			if ( inputModeIds[i]==id)
			{
				if ( inputModeHandlers[i]==null)
				{
					switch ( id )
					{
					case R.id.itemInputFitToScreen :
						inputModeHandlers[i]=new FitToScreenMode();
						break;
					case R.id.itemInputPan :
						inputModeHandlers[i]=new PanMode();
						break;
					case R.id.itemInputMouse :
						inputModeHandlers[i]=new MouseMode();
						break;
					case R.id.itemInputTouchPanTrackballMouse :
						inputModeHandlers[i]=new TouchPanTrackballMouse();
						break;
					case R.id.itemInputFullMouseControl:
						inputModeHandlers[i]=new FullMouseMode(vncCanvas, this);
						break;
					}
				}
				return inputModeHandlers[i];
			}
		}
		return null;
	}
	
	AbstractInputHandler getInputHandlerByName(String name)
	{
		AbstractInputHandler result = null;
		for (int id : inputModeIds) {
			AbstractInputHandler handler = getInputHandlerById( id );
			if (handler.getName().equals(name)) {
				result = handler;
				break;
			}
		}
		return result;
	}

	int absoluteXPosition = 0;

	int absoluteYPosition = 0;
	
	
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		
		
		switch (item.getItemId()) {
		case R.id.itemInfo:
			log.log(TAG, "Menu item: Information");
			vncCanvas.showConnectionInfo();
			return true;
		case R.id.itemColorMode:
			log.log(TAG, "Menu item: Select Color");
			selectColorModel();
			return true;
		case R.id.itemOneToOne:
			log.log(TAG, "Scale Mode: 1:1");
			inputHandler = getInputHandlerById(R.id.itemInputPan);
			item.setChecked(true);
			showPanningState();
			// Change to 1:1 scaling (which auto-centers)
			//vncCanvas.setScaleType(ScaleType.CENTER);
			Matrix matriz = new Matrix();
			matriz.setScale(1, 1);
			vncCanvas.setImageMatrix(matriz);
			vncCanvas.scrollTo(0,0);
			vncCanvas.setScaleType(ScaleType.MATRIX);
			connection.setScaleMode(ScaleType.MATRIX);
			updateInputMenu();
			// Reset the pan position to (0,0)
			//vncCanvas.scrollTo(-vncCanvas.getCenteredXOffset(),-vncCanvas.getCenteredYOffset());
			connection.save(database.getWritableDatabase());
			return true;
		case R.id.itemFitToScreen:
			log.log(TAG, "Scale Mode: Fit to Screen");
			inputHandler = getInputHandlerById(R.id.itemInputFitToScreen);
			item.setChecked(true);
			vncCanvas.setScaleType(ScaleType.FIT_CENTER);
			connection.setScaleMode(ScaleType.FIT_CENTER);
			absoluteXPosition = 0;
			absoluteYPosition = 0;
			updateInputMenu();
			vncCanvas.scrollTo(absoluteXPosition, absoluteYPosition);
			connection.save(database.getWritableDatabase());
			return true;
		case R.id.itemCenterMouse:
			log.log(TAG, "Menu item: Center Mouse");
			Display display=getWindowManager().getDefaultDisplay();
			vncCanvas.warpMouse( absoluteXPosition + display.getWidth()/2, absoluteYPosition + display.getHeight()/2);
			return true;
		case R.id.itemDisconnect:
			log.log(TAG, "Menu item: Disconnect");
			notificacion.notificar(getString(R.string.disconnected),getString(R.string.connection_closed));
			notificacion.eliminar();
			disconnect = true;
			vncCanvas.closeConnection();
			finish();
			return true;
		case R.id.itemCtrlAltDel:
			log.log(TAG, "Menu item: Ctrl-Alt-Del");
			vncCanvas.ctrlAltDel();
			return true;
		case R.id.itemFollowMouse:
			log.log(TAG, "Menu item: Follow Mouse");
			boolean newFollow = ! connection.getFollowMouse();
			item.setChecked(newFollow);
			connection.setFollowMouse(newFollow);
			if (newFollow) {
				panToMouse();
			}
			connection.save(database.getWritableDatabase());
			return true;
		case R.id.itemScreenshot:
			log.log(TAG, "Menu item: Screenshot");
			return Screenshot.nuevo(this, vncCanvas);
		case R.id.itemacelerometro:
			log.log(TAG, "Sensor Selected: acelerometer");
			if (item.isChecked()){
				sensor.quitar(Sensores.ACELEROMETRO);
				item.setChecked(false);
			}
			else{			
				sensor= Sensores.Instancia(this, vncCanvas);
				mi_menu.findItem(R.id.itembrujula).setChecked(false);
				mi_menu.findItem(R.id.itemorientacion).setChecked(false);
				sensor.quitar();
				item.setChecked(sensor.registrar(Sensores.ACELEROMETRO));
			}
			break;
		case R.id.itemorientacion:
			log.log(TAG, "Sensor Selected: orientation sensor");
			if (item.isChecked()){
				sensor.quitar(Sensores.ORIENTACION);
				item.setChecked(false);
			}else{
				sensor= Sensores.Instancia(this, vncCanvas);
				mi_menu.findItem(R.id.itemacelerometro).setChecked(false);
				mi_menu.findItem(R.id.itembrujula).setChecked(false);
				sensor.quitar();
				item.setChecked(sensor.registrar(Sensores.ORIENTACION));	
			}
			break;
		case R.id.itembrujula:
			log.log(TAG, "Sensor Selected: compass");
			if (item.isChecked()){
				sensor.quitar(Sensores.BRUJULA);
				item.setChecked(false);
			}else{
				sensor= Sensores.Instancia(this, vncCanvas);
				mi_menu.findItem(R.id.itemacelerometro).setChecked(false);
				mi_menu.findItem(R.id.itemorientacion).setChecked(false);
				sensor.quitar();
				item.setChecked(sensor.registrar(Sensores.BRUJULA));
			}
			break;
		case R.id.itemZoom:
			log.log(TAG, "Menu item: Zoom controls");
			if(!item.isChecked()){
				item.setChecked(true);
				FullMouseMode.zoom_control.show();
				FullMouseMode.reset.setVisibility(View.VISIBLE);
				notificacion.notificar(getString(R.string.T_zoom_controls), getString(R.string.I_zoom_controls));
			}else{
				item.setChecked(false);
				FullMouseMode.zoom_control.hide();
				FullMouseMode.reset.setVisibility(View.INVISIBLE);
				notificacion.notificar(getString(R.string.T_no_zoom_controls), getString(R.string.I_no_zoom_controls));
			}
			break;
		case R.id.itemPegar:
			log.log(TAG, "Menu item: Paste from local clipboard");
			Pegar.pegar(this, vncCanvas);
			break;
		case R.id.itemLog:
			Log.d("androidVNC", ""+AndroidLog.getsavelog());
			if(AndroidLog.getsavelog()){
				Intent intent = new Intent(this, WebViewActivity.class);
				this.startActivity(intent);
			}else{
				Toast.makeText(this, "No se guardó Log", Toast.LENGTH_SHORT).show();
			}
			break;
		default :
			AbstractInputHandler input=getInputHandlerById(item.getItemId());
			if ( input != null)
			{
				inputHandler=input;
				connection.setInputMode(input.getName());
				item.setChecked(true);
				showPanningState();
				connection.save(database.getWritableDatabase());
				return true;
			}
		}
		return super.onOptionsItemSelected(item);
	}
	
	@Override
	protected void onDestroy() {
		super.onDestroy();
		if (isFinishing())
		{
			vncCanvas.closeConnection();
			vncCanvas.onDestroy();
			database.close();
		}
	}
	
	@Override
	public boolean onKeyDown(int keyCode, KeyEvent evt) {
		if (keyCode == KeyEvent.KEYCODE_MENU)
			return super.onKeyDown( keyCode, evt);
		return inputHandler.onKeyDown(keyCode, evt);
	}
		
	@Override
	public boolean onKeyUp(int keyCode, KeyEvent evt) {
		if (keyCode == KeyEvent.KEYCODE_MENU)
			return super.onKeyUp( keyCode, evt);
		
		return inputHandler.onKeyUp(keyCode, evt);
	}

	public void showPanningState() {
		Toast.makeText(this, inputHandler.getHandlerDescription(), Toast.LENGTH_SHORT).show();
	}

	private boolean isFitToScreen() {
		return vncCanvas.getScaleType() == ScaleType.FIT_CENTER;
	}

	/* (non-Javadoc)
	 * @see android.app.Activity#onTrackballEvent(android.view.MotionEvent)
	 */
	@Override
	public boolean onTrackballEvent(MotionEvent event) {
		switch (event.getAction())
		{
		case MotionEvent.ACTION_DOWN:
			trackballButtonDown = true;
			break;
		case MotionEvent.ACTION_UP:
			trackballButtonDown = false;
			break;
		}
		return inputHandler.onTrackballEvent(event);
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		return inputHandler.onTouchEvent(event);
	}

	private void selectColorModel() {
		// Stop repainting the desktop
		// because the display is composited!
		vncCanvas.disableRepaints();
		
		String[] choices = new String[COLORMODEL.values().length];
		int currentSelection = -1;
		for (int i = 0; i < choices.length; i++) {
			COLORMODEL cm = COLORMODEL.values()[i];
			choices[i] = cm.toString();
			if (vncCanvas.isColorModel(cm))
				currentSelection = i;
		}

		final Dialog dialog = new Dialog(this);
		dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
		ListView list = new ListView(this);
		list.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_checked, choices));
		list.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
		list.setItemChecked(currentSelection, true);
		list.setOnItemClickListener(new OnItemClickListener() {
			public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
				dialog.dismiss();
				COLORMODEL cm = COLORMODEL.values()[arg2];
				vncCanvas.setColorModel(cm);
				connection.setColorModel(cm.nameString());
				connection.save(database.getWritableDatabase());
				Toast.makeText(VncCanvasActivity.this, getString(R.string.update_color_mode)+" "+ cm.toString(), Toast.LENGTH_SHORT).show();
			}
		});
		dialog.setOnDismissListener(new OnDismissListener() {
			@Override
			public void onDismiss(DialogInterface arg0) {
				Log.i(TAG, "Color Model Selector dismissed");
				log.log(TAG, "Color Model Selector dismissed");
				// Restore desktop repaints
				vncCanvas.enableRepaints();
			}
		});
		dialog.setContentView(list);
		dialog.show();		
	}
	
	float panTouchX;

	float panTouchY;

	/**
	 * Pan based on touch motions
	 * @param event
	 */
	private void pan(MotionEvent event) {
		float curX = event.getX();
		float curY = event.getY();
		int dX = (int) (panTouchX - curX);
		int dY = (int) (panTouchY - curY);

		// Prevent panning left or above desktop image
		if (absoluteXPosition + dX < 0)
			// dX = diff to 0
			dX = absoluteXPosition * -1;
		if (absoluteYPosition + dY < 0)
			// dY = diff to 0
			dY = absoluteYPosition * -1;

		// Prevent panning right or below desktop image
		if (absoluteXPosition + vncCanvas.getWidth() + dX > vncCanvas.getImageWidth())
			dX = 0;
		if (absoluteYPosition + vncCanvas.getHeight() + dY > vncCanvas.getImageHeight())
			dY = 0;

		absoluteXPosition += dX;
		absoluteYPosition += dY;
		vncCanvas.scrollBy(dX, dY);
	}

	boolean defaultKeyDownHandler( int keyCode, KeyEvent evt)
	{
		if ( vncCanvas.processLocalKeyEvent(keyCode, evt))
			return true;
		return super.onKeyDown(keyCode, evt);
	}
	
	boolean defaultKeyUpHandler( int keyCode, KeyEvent evt)
	{
		if ( vncCanvas.processLocalKeyEvent(keyCode, evt))
			return true;
		return super.onKeyUp(keyCode, evt);
	}
	
	boolean touchPan( MotionEvent event)
	{
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			panTouchX = event.getX();
			panTouchY = event.getY();
			break;
		case MotionEvent.ACTION_MOVE:
			pan(event);
			panTouchX = event.getX();
			panTouchY = event.getY();
			break;
		case MotionEvent.ACTION_UP:
			pan(event);
			break;
		}
		return true;		
	}
	
	boolean trackballMouse( MotionEvent evt)
	{
		int dx = (int)(evt.getX() * 6.01);
		int dy = (int)(evt.getY() * 6.01);
		
		dx = dx * dx * dx;
		dy = dy * dy * dy;
		
		evt.offsetLocation( vncCanvas.mouseX + dx - evt.getX(), vncCanvas.mouseY + dy - evt.getY());
		
		if (vncCanvas.processPointerEvent(evt,trackballButtonDown)) {
			return true;
		}
		return VncCanvasActivity.super.onTouchEvent(evt);		
	}
	
	/**
	 * Touches and dpad (trackball) pan the screen
	 * @author Michael A. MacDonald
	 *
	 */
	class PanMode implements AbstractInputHandler
	{

		/* (non-Javadoc)
		 * @see android.androidVNC.AbstractInputHandler#onKeyDown(int, android.view.KeyEvent)
		 */
		@Override
		public boolean onKeyDown(int keyCode, KeyEvent evt) {
			// DPAD KeyDown events are move MotionEvents in Panning Mode
			final int dPos = 100;
			boolean result=false;
			switch (keyCode) {
			case KeyEvent.KEYCODE_DPAD_CENTER:
				result=true;
				break;
			case KeyEvent.KEYCODE_DPAD_LEFT:
				onTouchEvent(MotionEvent.obtain(1, System.currentTimeMillis(), MotionEvent.ACTION_MOVE, panTouchX + dPos, panTouchY, 0));
				result=true;
				break;
			case KeyEvent.KEYCODE_DPAD_RIGHT:
				onTouchEvent(MotionEvent.obtain(1, System.currentTimeMillis(), MotionEvent.ACTION_MOVE, panTouchX - dPos, panTouchY, 0));
				result=true;
				break;
			case KeyEvent.KEYCODE_DPAD_UP:
				onTouchEvent(MotionEvent.obtain(1, System.currentTimeMillis(), MotionEvent.ACTION_MOVE, panTouchX, panTouchY + dPos, 0));
				result=true;
				break;
			case KeyEvent.KEYCODE_DPAD_DOWN:
				onTouchEvent(MotionEvent.obtain(1, System.currentTimeMillis(), MotionEvent.ACTION_MOVE, panTouchX, panTouchY - dPos, 0));
				result=true;
				break;
			default:
				result=defaultKeyDownHandler( keyCode, evt);
				break;
			}
			return result;
		}

		/* (non-Javadoc)
		 * @see android.androidVNC.AbstractInputHandler#onKeyUp(int, android.view.KeyEvent)
		 */
		@Override
		public boolean onKeyUp(int keyCode, KeyEvent evt) {
			// Ignore KeyUp events for DPAD keys in Panning Mode; trackball button switches to mouse mode
			switch (keyCode) {
			case KeyEvent.KEYCODE_DPAD_CENTER :
				inputHandler=getInputHandlerById(R.id.itemInputMouse);
				for ( MenuItem item : inputModeMenuItems)
				{
					if ( item.getItemId()==R.id.itemInputMouse) {
						item.setChecked( true );
						break;
					}
				}
				showPanningState();
				return true;
			case KeyEvent.KEYCODE_DPAD_LEFT:
				return true;
			case KeyEvent.KEYCODE_DPAD_RIGHT:
				return true;
			case KeyEvent.KEYCODE_DPAD_UP:
				return true;
			case KeyEvent.KEYCODE_DPAD_DOWN:
				return true;
			}
			return defaultKeyUpHandler( keyCode, evt);
		}

		/* (non-Javadoc)
		 * @see android.androidVNC.AbstractInputHandler#onTouchEvent(android.view.MotionEvent)
		 */
		@Override
		public boolean onTouchEvent(MotionEvent event) {
			return touchPan(event);
		}

		/* (non-Javadoc)
		 * @see android.androidVNC.AbstractInputHandler#onTrackballEvent(android.view.MotionEvent)
		 */
		@Override
		public boolean onTrackballEvent(MotionEvent evt) {
			return false;
		}

		/* (non-Javadoc)
		 * @see android.androidVNC.AbstractInputHandler#handlerDescription()
		 */
		@Override
		public CharSequence getHandlerDescription() {
			return getResources().getText(R.string.input_mode_panning);
		}

		/* (non-Javadoc)
		 * @see android.androidVNC.AbstractInputHandler#getName()
		 */
		@Override
		public String getName() {
			return "PAN_MODE";
		}
		
	}
	
	/**
	 * The touchscreen pans the screen; the trackball moves and clicks the mouse.
	 * @author Michael A. MacDonald
	 *
	 */
	public class TouchPanTrackballMouse implements AbstractInputHandler {

		/* (non-Javadoc)
		 * @see android.androidVNC.AbstractInputHandler#onKeyDown(int, android.view.KeyEvent)
		 */
		@Override
		public boolean onKeyDown(int keyCode, KeyEvent evt) {
			return defaultKeyDownHandler(keyCode, evt);
		}

		/* (non-Javadoc)
		 * @see android.androidVNC.AbstractInputHandler#onKeyUp(int, android.view.KeyEvent)
		 */
		@Override
		public boolean onKeyUp(int keyCode, KeyEvent evt) {
			return defaultKeyUpHandler(keyCode, evt);
		}

		/* (non-Javadoc)
		 * @see android.androidVNC.AbstractInputHandler#onTouchEvent(android.view.MotionEvent)
		 */
		@Override
		public boolean onTouchEvent(MotionEvent evt) {
			return touchPan(evt);
		}

		/* (non-Javadoc)
		 * @see android.androidVNC.AbstractInputHandler#onTrackballEvent(android.view.MotionEvent)
		 */
		@Override
		public boolean onTrackballEvent(MotionEvent evt) {
			return trackballMouse(evt);
		}

		/* (non-Javadoc)
		 * @see android.androidVNC.AbstractInputHandler#handlerDescription()
		 */
		@Override
		public CharSequence getHandlerDescription() {
			return getResources().getText(R.string.input_mode_touchpad_pan_trackball_mouse);
		}

		/* (non-Javadoc)
		 * @see android.androidVNC.AbstractInputHandler#getName()
		 */
		@Override
		public String getName() {
			return "TOUCH_PAN_TRACKBALL_MOUSE";
		}
		
	}

	static final String FIT_SCREEN_NAME = "FIT_SCREEN";
	
	/**
	 * In fit-to-screen mode, no panning.  Trackball and touchscreen work as mouse.
	 * @author Michael A. MacDonald
	 *
	 */
	public class FitToScreenMode implements AbstractInputHandler {

		/* (non-Javadoc)
		 * @see android.androidVNC.AbstractInputHandler#onKeyDown(int, android.view.KeyEvent)
		 */
		@Override
		public boolean onKeyDown(int keyCode, KeyEvent evt) {
			return defaultKeyDownHandler(keyCode, evt);
		}

		/* (non-Javadoc)
		 * @see android.androidVNC.AbstractInputHandler#onKeyUp(int, android.view.KeyEvent)
		 */
		@Override
		public boolean onKeyUp(int keyCode, KeyEvent evt) {
			return defaultKeyUpHandler(keyCode, evt);
		}

		/* (non-Javadoc)
		 * @see android.androidVNC.AbstractInputHandler#onTouchEvent(android.view.MotionEvent)
		 */
		@Override
		public boolean onTouchEvent(MotionEvent evt) {
			return false;
		}

		/* (non-Javadoc)
		 * @see android.androidVNC.AbstractInputHandler#onTrackballEvent(android.view.MotionEvent)
		 */
		@Override
		public boolean onTrackballEvent(MotionEvent evt) {
			return trackballMouse(evt);
		}

		/* (non-Javadoc)
		 * @see android.androidVNC.AbstractInputHandler#handlerDescription()
		 */
		@Override
		public CharSequence getHandlerDescription() {
			return getResources().getText(R.string.input_mode_fit_to_screen);
		}

		/* (non-Javadoc)
		 * @see android.androidVNC.AbstractInputHandler#getName()
		 */
		@Override
		public String getName() {
			return FIT_SCREEN_NAME;
		}
		
	}

	/**
	 * Touch screen controls, clicks the mouse.
	 * @author Michael A. MacDonald
	 *
	 */
	class MouseMode implements AbstractInputHandler {

		/* (non-Javadoc)
		 * @see android.androidVNC.AbstractInputHandler#onKeyDown(int, android.view.KeyEvent)
		 */
		@Override
		public boolean onKeyDown(int keyCode, KeyEvent evt) {
			if ( keyCode==KeyEvent.KEYCODE_DPAD_CENTER)
				return true;
			return defaultKeyDownHandler( keyCode, evt);
		}

		/* (non-Javadoc)
		 * @see android.androidVNC.AbstractInputHandler#onKeyUp(int, android.view.KeyEvent)
		 */
		@Override
		public boolean onKeyUp(int keyCode, KeyEvent evt) {
			if ( keyCode==KeyEvent.KEYCODE_DPAD_CENTER)
			{
				inputHandler=getInputHandlerById( R.id.itemInputPan);
				for ( MenuItem item : inputModeMenuItems)
				{
					if ( item.getItemId()==R.id.itemInputPan) {
						item.setChecked( true );
						break;
					}
				}
				showPanningState();
				return true;
			}
			return defaultKeyDownHandler( keyCode, evt);
		}

		/* (non-Javadoc)
		 * @see android.androidVNC.AbstractInputHandler#onTouchEvent(android.view.MotionEvent)
		 */
		@Override
		public boolean onTouchEvent(MotionEvent event) {
			// Mouse Pointer Control Mode
			// Pointer event is absolute coordinates.

			// Adjust coordinates for Android notification bar.
			event.offsetLocation(0, -1f * vncCanvas.getTop());

			// Adjust coordinates for panning position.
			event.offsetLocation(absoluteXPosition, absoluteYPosition);
			if (vncCanvas.processPointerEvent(event,true))
				return true;
			return VncCanvasActivity.super.onTouchEvent(event);
		}

		/* (non-Javadoc)
		 * @see android.androidVNC.AbstractInputHandler#onTrackballEvent(android.view.MotionEvent)
		 */
		@Override
		public boolean onTrackballEvent(MotionEvent evt) {
			return false;
		}

		/* (non-Javadoc)
		 * @see android.androidVNC.AbstractInputHandler#handlerDescription()
		 */
		@Override
		public CharSequence getHandlerDescription() {
			return getResources().getText(R.string.input_mode_mouse);
		}

		/* (non-Javadoc)
		 * @see android.androidVNC.AbstractInputHandler#getName()
		 */
		@Override
		public String getName() {
			return "MOUSE";
		}
		
	}

}
