/* * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.motorola.studio.android.emulator.ui.controls.maindisplay; import static com.motorola.studio.android.common.log.StudioLogger.info; import java.util.Properties; import org.eclipse.core.runtime.Platform; import org.eclipse.sequoyah.vnc.vncviewer.graphics.IRemoteDisplay; import org.eclipse.sequoyah.vnc.vncviewer.graphics.swt.SWTRemoteDisplay; import org.eclipse.swt.SWT; import org.eclipse.swt.events.ControlAdapter; import org.eclipse.swt.events.ControlEvent; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.KeyListener; import org.eclipse.swt.events.MouseAdapter; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.MouseListener; import org.eclipse.swt.events.MouseMoveListener; import org.eclipse.swt.events.MouseWheelListener; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Composite; import com.motorola.studio.android.adt.DDMSFacade; import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance; import com.motorola.studio.android.emulator.core.model.IInputLogic; import com.motorola.studio.android.emulator.logic.AndroidLogicUtils; import com.motorola.studio.android.emulator.skin.android.AndroidSkinTranslator; import com.motorola.studio.android.emulator.ui.controls.IAndroidComposite; import com.motorola.studio.android.emulator.ui.controls.UIHelper; import com.motorola.studio.android.emulator.ui.handlers.IHandlerConstants; import com.motorola.studio.android.nativeos.NativeUIUtils; /** * This class is the composite that holds the main display and it is shown by * the Emulator Main Display View. */ public class MainDisplayComposite extends Composite implements IAndroidComposite { /** * The zoom factor whose default value is 1.0 (100%) */ private double zoomFactor = 1.0; private double fitZoomfactor; // Minimum value to be used as zoom factor. This is necessary to avoid // divisions to zero private static final double MINIMUM_ZOOM_FACTOR = 0.0001; private static final double ZOOM_FIT = 0.0; /** * The flag indicating that Ctrl key is pressed */ private boolean ctrlPressed = false; /** * SWT key pressed/released events listener. */ private KeyListener keyListener; private MouseListener mouseListener; private MouseMoveListener mouseMoveListener; private IInputLogic androidInput; private boolean isMouseLeftButtonPressed; private boolean isFitToWindow; private IAndroidEmulatorInstance androidInstance; private Properties keyMap; /** * Constructor * * @param parent * composite * @param style * style * @param baseWidth * the default main display width * @param baseHeight * the default main display height */ public MainDisplayComposite(Composite parent, int style, int baseWidth, int baseHeight, IAndroidEmulatorInstance instance) { super(parent, style); androidInput = instance.getInputLogic(); androidInstance = instance; isMouseLeftButtonPressed = false; keyMap = AndroidSkinTranslator.getQwertyKeyMap(); addListener(); if (!Platform.getOS().equals(Platform.OS_MACOSX)) { hideEmulatorWindow(); } } private void hideEmulatorWindow() { int port = AndroidLogicUtils.getEmulatorPort(DDMSFacade.getSerialNumberByName(androidInstance .getName())); long windowHandle = NativeUIUtils.getWindowHandle(androidInstance.getName(), port); androidInstance.setWindowHandle(windowHandle); NativeUIUtils.hideWindow(windowHandle); } /* * (non-Javadoc) * @see org.eclipse.swt.widgets.Widget#dispose() */ @Override public void dispose() { if (androidInput != null) { androidInput.dispose(); } keyListener = null; mouseListener = null; mouseMoveListener = null; if (!Platform.getOS().equals(Platform.OS_MACOSX)) { long hnd = androidInstance.getWindowHandle(); if (hnd > 0) { NativeUIUtils.showWindow(hnd); NativeUIUtils.restoreWindow(hnd); } //Force update on redrawing androidInstance.setWindowHandle(0); } super.dispose(); } /** * Updates the composite size when zoom is changed. * * @param zoom * the zoom factor */ public void setZoomFactor(double zoomFactor) { info("Update detached view composite size"); if (zoomFactor == ZOOM_FIT) { isFitToWindow = true; } else { isFitToWindow = false; } this.zoomFactor = zoomFactor; } /** * Gets the zoom factor. * * @return zoom the zoom factor. */ public double getZoomFactor() { return zoomFactor; } /** * Applies the zoom factor to the components of the composite, updating the * composite size to hold totally the main display. */ public void applyZoomFactor() { SWTRemoteDisplay mainDisplay = UIHelper.getRemoteDisplayAssociatedToControl(this); IRemoteDisplay.Rotation rotation = mainDisplay.getRotation(); int baseHeight; int baseWidth; switch (rotation) { case ROTATION_90DEG_COUNTERCLOCKWISE: baseHeight = mainDisplay.getScreenWidth(); baseWidth = mainDisplay.getScreenHeight(); break; default: baseHeight = mainDisplay.getScreenHeight(); baseWidth = mainDisplay.getScreenWidth(); } int width; int height; if (isFitToWindow) { Rectangle clientArea = getParent().getClientArea(); if ((clientArea.width == 0) || (clientArea.height == 0)) { // zoom factor cannot be zero, otherwise an // IllegalArgumentException // is raised in some SWT methods fitZoomfactor = MINIMUM_ZOOM_FACTOR; } else { double widthRatio = (double) (clientArea.width) / baseWidth; double heightRatio = (double) (clientArea.height) / baseHeight; fitZoomfactor = Math.min(widthRatio, heightRatio); } width = new Double(baseWidth * fitZoomfactor).intValue(); height = new Double(baseHeight * fitZoomfactor).intValue(); if (mainDisplay != null) { mainDisplay.setZoomFactor(fitZoomfactor); } } else { width = new Double(baseWidth * zoomFactor).intValue(); height = new Double(baseHeight * zoomFactor).intValue(); if (mainDisplay != null) { mainDisplay.setZoomFactor(zoomFactor); } } setSize(width, height); } /** * Adds listener for SWT events. */ private void addListener() { // add listener to handle keyboard key pressing keyListener = new KeyListener() { public void keyPressed(KeyEvent arg0) { int keyCode = arg0.keyCode; if (keyCode == SWT.CTRL) { ctrlPressed = true; } else { // send message to emulator androidInput.sendKey(arg0.character, keyCode, keyMap); } } public void keyReleased(KeyEvent arg0) { int keyCode = arg0.keyCode; if (keyCode == SWT.CTRL) { ctrlPressed = false; } } }; // listener to change the zoom factor using Ctrl + Mouse Wheel addMouseWheelListener(new MouseWheelListener() { public void mouseScrolled(MouseEvent event) { if (ctrlPressed) { if ((event.count > 0) && (zoomFactor < IHandlerConstants.MAXIMUM_ZOOM)) { // increase zoom factor setZoomFactor(zoomFactor + IHandlerConstants.STEP_ZOOM); applyZoomFactor(); } else if ((event.count < 0) && (zoomFactor > IHandlerConstants.MINIMUM_ZOOM)) { // decrease zoom factor setZoomFactor(zoomFactor - IHandlerConstants.STEP_ZOOM); applyZoomFactor(); } } } }); mouseListener = new MouseAdapter() { /** * @see org.eclipse.swt.events.MouseListener#mouseUp(MouseEvent) */ @Override public void mouseUp(MouseEvent e) { handleMouseUp(e); } /** * @see org.eclipse.swt.events.MouseListener#mouseDown(MouseEvent) */ @Override public void mouseDown(MouseEvent e) { setFocus(); handleMouseDown(e); } }; mouseMoveListener = new MouseMoveListener() { /** * @see org.eclipse.swt.events.MouseMoveListener#mouseMove(MouseEvent) */ public void mouseMove(MouseEvent e) { handleMouseMove(e); } }; getParent().addControlListener(new ControlAdapter() { /* * (non-Javadoc) * @see org.eclipse.swt.events.ControlAdapter#controlResized(org.eclipse.swt.events.ControlEvent) */ @Override public void controlResized(ControlEvent event) { if (isFitToWindow) { applyZoomFactor(); } } }); } /* * (non-Javadoc) * @see com.motorola.studio.android.emulator.ui.controls.IAndroidComposite#applyLayout(java.lang.String) */ public void applyLayout(String layoutName) { //do nothing } /** * Gets the listener that handles SWT key pressing and releasing events. * * @return the KeyListener object */ public KeyListener getKeyListener() { return keyListener; } /** * Gets the listener that handles SWT mouse clicking events. * * @return the MouseListener object */ public MouseListener getMouseListener() { return mouseListener; } /** * Gets the listener that handles SWT mouse moving events. * * @return the MouseMoveListener object */ public MouseMoveListener getMouseMoveListener() { return mouseMoveListener; } /** * Handles the mouse up event on the skin composite * * @param e * The mouse up event */ private void handleMouseUp(MouseEvent e) { if (e.button == 1) { isMouseLeftButtonPressed = false; UIHelper.ajustCoordinates(e, this); androidInput.sendMouseUp(e.x, e.y); } } /** * Handles the mouse down event on the skin composite * * @param e * The mouse down event */ private void handleMouseDown(MouseEvent e) { if (e.button == 1) { UIHelper.ajustCoordinates(e, this); androidInput.sendMouseDown(e.x, e.y); isMouseLeftButtonPressed = true; } } /** * Handles the mouse move event on the skin composite * * @param e * The mouse move event */ private void handleMouseMove(MouseEvent e) { if (isMouseLeftButtonPressed) { UIHelper.ajustCoordinates(e, this); androidInput.sendMouseMove(e.x, e.y); } } public boolean isFitToWindowSelected() { return isFitToWindow; } }