| Index: content/public/android/java/src/org/chromium/content/browser/InsertionHandleController.java
|
| diff --git a/content/public/android/java/src/org/chromium/content/browser/InsertionHandleController.java b/content/public/android/java/src/org/chromium/content/browser/InsertionHandleController.java
|
| index 8d84fded508d8aa45b404effe22efc0175542f45..2dd4008a0c5f5f0b774f9956ac38be635f4233d2 100644
|
| --- a/content/public/android/java/src/org/chromium/content/browser/InsertionHandleController.java
|
| +++ b/content/public/android/java/src/org/chromium/content/browser/InsertionHandleController.java
|
| @@ -4,14 +4,35 @@
|
|
|
| package org.chromium.content.browser;
|
|
|
| +import android.content.ClipboardManager;
|
| import android.content.Context;
|
| +import android.content.res.TypedArray;
|
| +import android.graphics.drawable.Drawable;
|
| +import android.view.Gravity;
|
| +import android.view.LayoutInflater;
|
| import android.view.View;
|
| +import android.view.View.OnClickListener;
|
| +import android.view.ViewGroup;
|
| +import android.view.ViewGroup.LayoutParams;
|
| +import android.widget.PopupWindow;
|
|
|
| -class InsertionHandleController {
|
| +/**
|
| + * CursorController for inserting text at the cursor position.
|
| + */
|
| +abstract class InsertionHandleController implements CursorController {
|
| +
|
| + /** The handle view, lazily created when first shown */
|
| + private HandleView mHandle;
|
|
|
| /** The view over which the insertion handle should be shown */
|
| private View mParent;
|
|
|
| + /** True iff the insertion handle is currently showing */
|
| + private boolean mIsShowing;
|
| +
|
| + /** True iff the insertion handle can be shown automatically when selection changes */
|
| + private boolean mAllowAutomaticShowing;
|
| +
|
| private Context mContext;
|
|
|
| InsertionHandleController(View parent) {
|
| @@ -19,8 +40,251 @@ class InsertionHandleController {
|
| mContext = parent.getContext();
|
| }
|
|
|
| + /** Allows the handle to be shown automatically when cursor position changes */
|
| + void allowAutomaticShowing() {
|
| + mAllowAutomaticShowing = true;
|
| + }
|
| +
|
| /** Disallows the handle from being shown automatically when cursor position changes */
|
| - public void hideAndDisallowAutomaticShowing() {
|
| - // TODO(olilan): add method implementation (this is just a stub for ImeAdapter).
|
| + void hideAndDisallowAutomaticShowing() {
|
| + hide();
|
| + mAllowAutomaticShowing = false;
|
| + }
|
| +
|
| + /**
|
| + * Sets the position and shows the handle.
|
| + * @param x1
|
| + * @param y1
|
| + */
|
| + void showHandleAt(int x, int y) {
|
| + createHandleIfNeeded();
|
| + setHandlePosition(x, y);
|
| + showHandleIfNeeded();
|
| + }
|
| +
|
| + void showPastePopup() {
|
| + if (mIsShowing) {
|
| + mHandle.showPastePopupWindow();
|
| + }
|
| + }
|
| +
|
| + void showHandleWithPastePopupAt(int x, int y) {
|
| + showHandleAt(x, y);
|
| + showPastePopup();
|
| + }
|
| +
|
| + /** Shows the handle at the given coordinates, as long as automatic showing is allowed */
|
| + void onCursorPositionChanged(int x, int y) {
|
| + if (mAllowAutomaticShowing) {
|
| + showHandleAt(x, y);
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Moves the handle so that it points at the given coordinates.
|
| + * @param x
|
| + * @param y
|
| + */
|
| + void setHandlePosition(int x, int y) {
|
| + mHandle.positionAt(x, y);
|
| + }
|
| +
|
| + @Override
|
| + public void onTouchModeChanged(boolean isInTouchMode) {
|
| + if (!isInTouchMode) {
|
| + hide();
|
| + }
|
| + }
|
| +
|
| + @Override
|
| + public void hide() {
|
| + if (mIsShowing) {
|
| + if (mHandle != null) mHandle.hide();
|
| + mIsShowing = false;
|
| + }
|
| + }
|
| +
|
| + @Override
|
| + public boolean isShowing() {
|
| + return mIsShowing;
|
| + }
|
| +
|
| + @Override
|
| + public void beforeStartUpdatingPosition(HandleView handle) {}
|
| +
|
| + @Override
|
| + public void updatePosition(HandleView handle, int x, int y) {
|
| + setCursorPosition(x, y);
|
| + }
|
| +
|
| + /**
|
| + * The concrete implementation must cause the cursor position to move to the given
|
| + * coordinates and (possibly asynchronously) set the insertion handle position
|
| + * after the cursor position change is made via showHandleAt(x,y).
|
| + * @param x
|
| + * @param y
|
| + */
|
| + protected abstract void setCursorPosition(int x, int y);
|
| +
|
| + /** Pastes the contents of clipboard at the current insertion point */
|
| + protected abstract void paste();
|
| +
|
| + /** Returns the current line height in pixels */
|
| + protected abstract int getLineHeight();
|
| +
|
| + @Override
|
| + public void onDetached() {}
|
| +
|
| + boolean canPaste() {
|
| + return ((ClipboardManager)mContext.getSystemService(
|
| + Context.CLIPBOARD_SERVICE)).hasPrimaryClip();
|
| + }
|
| +
|
| + private void createHandleIfNeeded() {
|
| + if (mHandle == null) mHandle = new HandleView(this, HandleView.CENTER, mParent);
|
| + }
|
| +
|
| + private void showHandleIfNeeded() {
|
| + if (!mIsShowing) {
|
| + mIsShowing = true;
|
| + mHandle.show();
|
| + }
|
| + }
|
| +
|
| + /*
|
| + * This class is based on TextView.PastePopupMenu.
|
| + */
|
| + class PastePopupMenu implements OnClickListener {
|
| + private final PopupWindow mContainer;
|
| + private int mPositionX;
|
| + private int mPositionY;
|
| + private View[] mPasteViews;
|
| + private int[] mPasteViewLayouts;
|
| +
|
| + public PastePopupMenu() {
|
| + mContainer = new PopupWindow(mContext, null,
|
| + android.R.attr.textSelectHandleWindowStyle);
|
| + mContainer.setSplitTouchEnabled(true);
|
| + mContainer.setClippingEnabled(false);
|
| +
|
| + mContainer.setWidth(ViewGroup.LayoutParams.WRAP_CONTENT);
|
| + mContainer.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
|
| +
|
| + final int[] POPUP_LAYOUT_ATTRS = {
|
| + android.R.attr.textEditPasteWindowLayout,
|
| + android.R.attr.textEditNoPasteWindowLayout,
|
| + android.R.attr.textEditSidePasteWindowLayout,
|
| + android.R.attr.textEditSideNoPasteWindowLayout,
|
| + };
|
| +
|
| + mPasteViews = new View[POPUP_LAYOUT_ATTRS.length];
|
| + mPasteViewLayouts = new int[POPUP_LAYOUT_ATTRS.length];
|
| +
|
| + TypedArray attrs = mContext.obtainStyledAttributes(POPUP_LAYOUT_ATTRS);
|
| + for (int i = 0; i < attrs.length(); ++i) {
|
| + mPasteViewLayouts[i] = attrs.getResourceId(attrs.getIndex(i), 0);
|
| + }
|
| + attrs.recycle();
|
| + }
|
| +
|
| + private int viewIndex(boolean onTop) {
|
| + return (onTop ? 0 : 1<<1) + (canPaste() ? 0 : 1 << 0);
|
| + }
|
| +
|
| + private void updateContent(boolean onTop) {
|
| + final int viewIndex = viewIndex(onTop);
|
| + View view = mPasteViews[viewIndex];
|
| +
|
| + if (view == null) {
|
| + final int layout = mPasteViewLayouts[viewIndex];
|
| + LayoutInflater inflater = (LayoutInflater)mContext.
|
| + getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
| + if (inflater != null) {
|
| + view = inflater.inflate(layout, null);
|
| + }
|
| +
|
| + if (view == null) {
|
| + throw new IllegalArgumentException("Unable to inflate TextEdit paste window");
|
| + }
|
| +
|
| + final int size = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
|
| + view.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
|
| + ViewGroup.LayoutParams.WRAP_CONTENT));
|
| + view.measure(size, size);
|
| +
|
| + view.setOnClickListener(this);
|
| +
|
| + mPasteViews[viewIndex] = view;
|
| + }
|
| +
|
| + mContainer.setContentView(view);
|
| + }
|
| +
|
| + void show() {
|
| + updateContent(true);
|
| + positionAtCursor();
|
| + }
|
| +
|
| + void hide() {
|
| + mContainer.dismiss();
|
| + }
|
| +
|
| + boolean isShowing() {
|
| + return mContainer.isShowing();
|
| + }
|
| +
|
| + @Override
|
| + public void onClick(View v) {
|
| + if (canPaste()) {
|
| + paste();
|
| + }
|
| + hide();
|
| + }
|
| +
|
| + void positionAtCursor() {
|
| + View contentView = mContainer.getContentView();
|
| + int width = contentView.getMeasuredWidth();
|
| + int height = contentView.getMeasuredHeight();
|
| +
|
| + int lineHeight = getLineHeight();
|
| +
|
| + mPositionX = (int) (mHandle.getAdjustedPositionX() - width / 2.0f);
|
| + mPositionY = mHandle.getAdjustedPositionY() - height - lineHeight;
|
| +
|
| + final int[] coords = new int[2];
|
| + mParent.getLocationInWindow(coords);
|
| + coords[0] += mPositionX;
|
| + coords[1] += mPositionY;
|
| +
|
| + final int screenWidth = mContext.getResources().getDisplayMetrics().widthPixels;
|
| + if (coords[1] < 0) {
|
| + updateContent(false);
|
| + // Update dimensions from new view
|
| + contentView = mContainer.getContentView();
|
| + width = contentView.getMeasuredWidth();
|
| + height = contentView.getMeasuredHeight();
|
| +
|
| + // Vertical clipping, move under edited line and to the side of insertion cursor
|
| + // TODO bottom clipping in case there is no system bar
|
| + coords[1] += height;
|
| + coords[1] += lineHeight;
|
| +
|
| + // Move to right hand side of insertion cursor by default. TODO RTL text.
|
| + final Drawable handle = mHandle.getDrawable();
|
| + final int handleHalfWidth = handle.getIntrinsicWidth() / 2;
|
| +
|
| + if (mHandle.getAdjustedPositionX() + width < screenWidth) {
|
| + coords[0] += handleHalfWidth + width / 2;
|
| + } else {
|
| + coords[0] -= handleHalfWidth + width / 2;
|
| + }
|
| + } else {
|
| + // Horizontal clipping
|
| + coords[0] = Math.max(0, coords[0]);
|
| + coords[0] = Math.min(screenWidth - width, coords[0]);
|
| + }
|
| +
|
| + mContainer.showAtLocation(mParent, Gravity.NO_GRAVITY, coords[0], coords[1]);
|
| + }
|
| }
|
| }
|
|
|