/** * Copyright (C) 2012 Iordan Iordanov * Copyright (C) 2009 Michael A. MacDonald * * 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. */ package com.iiordanov.bVNC; import android.graphics.Matrix; import android.util.Log; import android.widget.Toast; import android.widget.ImageView.ScaleType; import com.iiordanov.bVNC.*; import com.iiordanov.freebVNC.*; import com.iiordanov.aRDP.*; import com.iiordanov.freeaRDP.*; import com.iiordanov.aSPICE.*; import com.iiordanov.freeaSPICE.*; /** * @author Michael A. MacDonald */ class ZoomScaling extends AbstractScaling { static final String TAG = "ZoomScaling"; private Matrix matrix; int canvasXOffset; int canvasYOffset; float scaling; float minimumScale; /** * @param id * @param scaleType */ public ZoomScaling() { super(R.id.itemZoomable, ScaleType.MATRIX); matrix = new Matrix(); scaling = 1; } /* (non-Javadoc) * @see com.iiordanov.bVNC.AbstractScaling#getDefaultHandlerId() */ @Override int getDefaultHandlerId() { return R.id.itemInputTouchPanZoomMouse; } /* (non-Javadoc) * @see com.iiordanov.bVNC.AbstractScaling#isAbleToPan() */ @Override boolean isAbleToPan() { return true; } /* (non-Javadoc) * @see com.iiordanov.bVNC.AbstractScaling#isValidInputMode(int) */ @Override boolean isValidInputMode(int mode) { // return mode == R.id.itemInputTouchPanZoomMouse; return true; } /** * Call after scaling and matrix have been changed to resolve scrolling * @param activity */ private void resolveZoom(RemoteCanvas canvas) { canvas.scrollToAbsolute(); //activity.vncCanvas.pan(0,0); } /* (non-Javadoc) * @see com.iiordanov.bVNC.AbstractScaling#zoomIn(com.iiordanov.bVNC.RemoteCanvasActivity) */ @Override void zoomIn(RemoteCanvasActivity activity) { resetMatrix(); standardizeScaling(); scaling += 0.25; if (scaling > 4.0) { scaling = (float)4.0; activity.zoomer.setIsZoomInEnabled(false); } activity.zoomer.setIsZoomOutEnabled(true); matrix.postScale(scaling, scaling); //Log.v(TAG,String.format("before set matrix scrollx = %d scrolly = %d", activity.vncCanvas.getScrollX(), activity.vncCanvas.getScrollY())); activity.getCanvas().setImageMatrix(matrix); resolveZoom(activity.getCanvas()); } /* (non-Javadoc) * @see com.iiordanov.bVNC.AbstractScaling#getScale() */ @Override float getScale() { return scaling; } /* (non-Javadoc) * @see com.iiordanov.bVNC.AbstractScaling#zoomOut(com.iiordanov.bVNC.RemoteCanvasActivity) */ @Override void zoomOut(RemoteCanvasActivity activity) { resetMatrix(); standardizeScaling(); scaling -= 0.25; if (scaling < minimumScale) { scaling = minimumScale; activity.zoomer.setIsZoomOutEnabled(false); } activity.zoomer.setIsZoomInEnabled(true); matrix.postScale(scaling, scaling); //Log.v(TAG,String.format("before set matrix scrollx = %d scrolly = %d", activity.vncCanvas.getScrollX(), activity.vncCanvas.getScrollY())); activity.getCanvas().setImageMatrix(matrix); //Log.v(TAG,String.format("after set matrix scrollx = %d scrolly = %d", activity.vncCanvas.getScrollX(), activity.vncCanvas.getScrollY())); resolveZoom(activity.getCanvas()); } /* (non-Javadoc) * @see com.iiordanov.bVNC.AbstractScaling#adjust(com.iiordanov.bVNC.RemoteCanvasActivity, float, float, float) */ @Override public void adjust(RemoteCanvasActivity activity, float scaleFactor, float fx, float fy) { float oldScale; float newScale = scaleFactor * scaling; if (scaleFactor < 1) { if (newScale < minimumScale) { newScale = minimumScale; activity.zoomer.setIsZoomOutEnabled(false); } activity.zoomer.setIsZoomInEnabled(true); } else { if (newScale > 4) { newScale = 4; activity.zoomer.setIsZoomInEnabled(false); } activity.zoomer.setIsZoomOutEnabled(true); } RemoteCanvas canvas = activity.getCanvas(); // ax is the absolute x of the focus int xPan = canvas.absoluteXPosition; float ax = (fx / scaling) + xPan; float newXPan = (scaling * xPan - scaling * ax + newScale * ax)/newScale; int yPan = canvas.absoluteYPosition; float ay = (fy / scaling) + yPan; float newYPan = (scaling * yPan - scaling * ay + newScale * ay)/newScale; // Here we do snapping to 1:1. If we are approaching scale = 1, we snap to it. oldScale = scaling; if ( (newScale > 0.90f && newScale < 1.00f) || (newScale > 1.00f && newScale < 1.10f) ) { newScale = 1.f; // Only if oldScale is outside the snap region, do we inform the user. if (oldScale < 0.90f || oldScale > 1.10f) canvas.displayShortToastMessage(R.string.snap_one_to_one); } resetMatrix(); scaling = newScale; matrix.postScale(scaling, scaling); canvas.setImageMatrix(matrix); resolveZoom(canvas); // Only if we have actually scaled do we pan and potentially set mouse position. if (oldScale != newScale) { canvas.pan((int)(newXPan - xPan), (int)(newYPan - yPan)); canvas.getPointer().mouseFollowPan(); } } private void resetMatrix() { matrix.reset(); matrix.preTranslate(canvasXOffset, canvasYOffset); } /** * Set scaling to one of the clicks on the zoom scale */ private void standardizeScaling() { scaling = ((float)((int)(scaling * 4))) / 4; } /* (non-Javadoc) * @see com.iiordanov.bVNC.AbstractScaling#setScaleTypeForActivity(com.iiordanov.bVNC.RemoteCanvasActivity) */ @Override void setScaleTypeForActivity(RemoteCanvasActivity activity) { super.setScaleTypeForActivity(activity); RemoteCanvas canvas = activity.getCanvas(); canvasXOffset = -canvas.getCenteredXOffset(); canvasYOffset = -canvas.getCenteredYOffset(); canvas.computeShiftFromFullToView (); minimumScale = canvas.getMinimumScale(); scaling = 1.f; resetMatrix(); canvas.setImageMatrix(matrix); resolveZoom(canvas); } }