1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2025-12-24 06:20:43 -08:00
emacs/java/org/gnu/emacs/EmacsWindow.java
Po Lu 2b87ab7b27 Update Android port
* java/Makefile.in (clean): Fix distclean and bootstrap-clean rules.
* java/debug.sh (jdb_port):
(attach_existing):
(num_pids):
(line): Add new options to upload a gdbserver binary to the device.

* java/org/gnu/emacs/EmacsActivity.java (EmacsActivity): Make
focusedActivities public.
* java/org/gnu/emacs/EmacsContextMenu.java (EmacsContextMenu):
New class.
* java/org/gnu/emacs/EmacsDrawRectangle.java (perform): Fix
bounds computation.
* java/org/gnu/emacs/EmacsGC.java (markDirty): Set stroke width
explicitly.
* java/org/gnu/emacs/EmacsService.java (EmacsService)
(getLocationOnScreen, nameKeysym): New functions.
* java/org/gnu/emacs/EmacsView.java (EmacsView): Disable focus
highlight.
(onCreateContextMenu, popupMenu, cancelPopupMenu): New
functions.
* java/org/gnu/emacs/EmacsWindow.java (EmacsWindow): Implement a
kind of ``override redirect'' window for tooltips.
* src/android.c (struct android_emacs_service): New method
`name_keysym'.
(android_run_select_thread, android_init_events):
(android_select): Release select thread on semaphores instead of
signals to avoid one nasty race on SIGUSR2 delivery.
(android_init_emacs_service): Initialize new method.
(android_create_window): Handle CW_OVERRIDE_REDIRECT.
(android_move_resize_window, android_map_raised)
(android_translate_coordinates, android_get_keysym_name)
(android_build_string, android_exception_check): New functions.
* src/android.h: Update prototypes.

* src/androidfns.c (android_set_parent_frame, Fx_create_frame)
(unwind_create_tip_frame, android_create_tip_frame)
(android_hide_tip, compute_tip_xy, Fx_show_tip, Fx_hide_tip)
(syms_of_androidfns): Implement tooltips and iconification
reporting.

* src/androidgui.h (enum android_window_value_mask): Add
CWOverrideRedirect.
(struct android_set_window_attributes): Add `override_redirect'.
(ANDROID_IS_MODIFIER_KEY): Recognize Caps Lock.

* src/androidmenu.c (struct android_emacs_context_menu): New
struct.
(android_init_emacs_context_menu, android_unwind_local_frame)
(android_push_local_frame, android_menu_show, init_androidmenu):
New functions.

* src/androidterm.c (handle_one_android_event): Fix NULL pointer
dereference.
(android_fullscreen_hook): Handle fullscreen correctly.
(android_draw_box_rect): Fix top line.
(get_keysym_name): Implement function.
(android_create_terminal): Remove scroll bar stubs and add menu
hook.

* src/androidterm.h: Update prototypes.
* src/emacs.c (android_emacs_init): Initialize androidmenu.c.
* xcompile/Makefile.in: Fix clean rules.
2023-01-14 22:12:16 +08:00

1029 lines
24 KiB
Java

/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
Copyright (C) 2023 Free Software Foundation, Inc.
This file is part of GNU Emacs.
GNU Emacs 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 3 of the License, or (at
your option) any later version.
GNU Emacs 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 GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
package org.gnu.emacs;
import java.lang.IllegalStateException;
import java.util.ArrayList;
import java.util.List;
import java.util.HashMap;
import android.content.Context;
import android.graphics.Rect;
import android.graphics.Canvas;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.graphics.PixelFormat;
import android.view.View;
import android.view.ViewManager;
import android.view.ViewGroup;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.InputDevice;
import android.view.WindowManager;
import android.content.Intent;
import android.util.Log;
import android.os.Build;
/* This defines a window, which is a handle. Windows represent a
rectangular subset of the screen with their own contents.
Windows either have a parent window, in which case their views are
attached to the parent's view, or are "floating", in which case
their views are attached to the parent activity (if any), else
nothing.
Views are also drawables, meaning they can accept drawing
requests. */
/* Help wanted. What does not work includes `EmacsView.raise',
`EmacsView.lower', reparenting a window onto another window.
All three are likely undocumented restrictions within
EmacsSurface. */
public class EmacsWindow extends EmacsHandleObject
implements EmacsDrawable
{
private static final String TAG = "EmacsWindow";
private class Coordinate
{
/* Integral coordinate. */
int x, y;
Coordinate (int x, int y)
{
this.x = x;
this.y = y;
}
};
/* The view associated with the window. */
public EmacsView view;
/* The geometry of the window. */
private Rect rect;
/* The parent window, or null if it is the root window. */
public EmacsWindow parent;
/* List of all children in stacking order. This must be kept
consistent! */
public ArrayList<EmacsWindow> children;
/* Map between pointer identifiers and last known position. Used to
compute which pointer changed upon a touch event. */
private HashMap<Integer, Coordinate> pointerMap;
/* The window consumer currently attached, if it exists. */
private EmacsWindowAttachmentManager.WindowConsumer attached;
/* The window background scratch GC. foreground is always the
window background. */
private EmacsGC scratchGC;
/* The button state and keyboard modifier mask at the time of the
last button press or release event. */
private int lastButtonState, lastModifiers;
/* Whether or not the window is mapped, and whether or not it is
deiconified. */
private boolean isMapped, isIconified;
/* Whether or not to ask for focus upon being mapped, and whether or
not the window should be focusable. */
private boolean dontFocusOnMap, dontAcceptFocus;
/* Whether or not the window is override-redirect. An
override-redirect window always has its own system window. */
private boolean overrideRedirect;
/* The window manager that is the parent of this window. NULL if
there is no such window manager. */
private WindowManager windowManager;
public
EmacsWindow (short handle, final EmacsWindow parent, int x, int y,
int width, int height, boolean overrideRedirect)
{
super (handle);
rect = new Rect (x, y, x + width, y + height);
pointerMap = new HashMap<Integer, Coordinate> ();
/* Create the view from the context's UI thread. The window is
unmapped, so the view is GONE. */
view = EmacsService.SERVICE.getEmacsView (this, View.GONE,
parent == null);
this.parent = parent;
this.overrideRedirect = overrideRedirect;
/* Create the list of children. */
children = new ArrayList<EmacsWindow> ();
if (parent != null)
{
parent.children.add (this);
EmacsService.SERVICE.runOnUiThread (new Runnable () {
@Override
public void
run ()
{
parent.view.addView (view);
}
});
}
scratchGC = new EmacsGC ((short) 0);
}
public void
changeWindowBackground (int pixel)
{
/* scratchGC is used as the argument to a FillRectangles req. */
scratchGC.foreground = pixel;
scratchGC.markDirty ();
}
public Rect
getGeometry ()
{
synchronized (this)
{
/* Huh, this is it. */
return rect;
}
}
@Override
public void
destroyHandle () throws IllegalStateException
{
if (parent != null)
parent.children.remove (this);
EmacsActivity.invalidateFocus ();
if (!children.isEmpty ())
throw new IllegalStateException ("Trying to destroy window with "
+ "children!");
/* Remove the view from its parent and make it invisible. */
EmacsService.SERVICE.runOnUiThread (new Runnable () {
public void
run ()
{
ViewManager parent;
EmacsWindowAttachmentManager manager;
if (EmacsActivity.focusedWindow == EmacsWindow.this)
EmacsActivity.focusedWindow = null;
manager = EmacsWindowAttachmentManager.MANAGER;
view.setVisibility (View.GONE);
/* If the window manager is set, use that instead. */
if (windowManager != null)
parent = windowManager;
else
parent = (ViewManager) view.getParent ();
windowManager = null;
if (parent != null)
parent.removeView (view);
manager.detachWindow (EmacsWindow.this);
}
});
super.destroyHandle ();
}
public void
setConsumer (EmacsWindowAttachmentManager.WindowConsumer consumer)
{
attached = consumer;
}
public EmacsWindowAttachmentManager.WindowConsumer
getAttachedConsumer ()
{
return attached;
}
public void
viewLayout (int left, int top, int right, int bottom)
{
int rectWidth, rectHeight;
synchronized (this)
{
rect.left = left;
rect.top = top;
rect.right = right;
rect.bottom = bottom;
}
rectWidth = right - left;
rectHeight = bottom - top;
EmacsNative.sendConfigureNotify (this.handle,
System.currentTimeMillis (),
left, top, rectWidth,
rectHeight);
}
public void
requestViewLayout ()
{
/* This is necessary because otherwise subsequent drawing on the
Emacs thread may be lost. */
view.explicitlyDirtyBitmap (rect);
EmacsService.SERVICE.runOnUiThread (new Runnable () {
@Override
public void
run ()
{
if (overrideRedirect)
/* Set the layout parameters again. */
view.setLayoutParams (getWindowLayoutParams ());
view.mustReportLayout = true;
view.requestLayout ();
}
});
}
public void
resizeWindow (int width, int height)
{
synchronized (this)
{
rect.right = rect.left + width;
rect.bottom = rect.top + height;
requestViewLayout ();
}
}
public void
moveWindow (int x, int y)
{
int width, height;
synchronized (this)
{
width = rect.width ();
height = rect.height ();
rect.left = x;
rect.top = y;
rect.right = x + width;
rect.bottom = y + height;
requestViewLayout ();
}
}
private WindowManager.LayoutParams
getWindowLayoutParams ()
{
WindowManager.LayoutParams params;
int flags, type;
Rect rect;
flags = 0;
rect = getGeometry ();
flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
params
= new WindowManager.LayoutParams (rect.width (), rect.height (),
rect.left, rect.top,
type, flags,
PixelFormat.RGBA_8888);
params.gravity = Gravity.TOP | Gravity.LEFT;
return params;
}
private Context
findSuitableActivityContext ()
{
/* Find a recently focused activity. */
if (!EmacsActivity.focusedActivities.isEmpty ())
return EmacsActivity.focusedActivities.get (0);
/* Return the service context, which probably won't work. */
return EmacsService.SERVICE;
}
public void
mapWindow ()
{
if (isMapped)
return;
isMapped = true;
if (parent == null)
{
EmacsService.SERVICE.runOnUiThread (new Runnable () {
@Override
public void
run ()
{
EmacsWindowAttachmentManager manager;
WindowManager windowManager;
Context ctx;
Object tem;
WindowManager.LayoutParams params;
/* Make the view visible, first of all. */
view.setVisibility (View.VISIBLE);
if (!overrideRedirect)
{
manager = EmacsWindowAttachmentManager.MANAGER;
/* If parent is the root window, notice that there are new
children available for interested activites to pick
up. */
manager.registerWindow (EmacsWindow.this);
if (!getDontFocusOnMap ())
/* Eventually this should check no-focus-on-map. */
view.requestFocus ();
}
else
{
/* But if the window is an override-redirect window,
then:
- Find an activity that is currently active.
- Map the window as a panel on top of that
activity using the system window manager. */
ctx = findSuitableActivityContext ();
tem = ctx.getSystemService (Context.WINDOW_SERVICE);
windowManager = (WindowManager) tem;
/* Calculate layout parameters. */
params = getWindowLayoutParams ();
view.setLayoutParams (params);
/* Attach the view. */
try
{
windowManager.addView (view, params);
/* Record the window manager being used in the
EmacsWindow object. */
EmacsWindow.this.windowManager = windowManager;
}
catch (Exception e)
{
Log.w (TAG,
"failed to attach override-redirect window, " + e);
}
}
}
});
}
else
{
/* Do the same thing as above, but don't register this
window. */
EmacsService.SERVICE.runOnUiThread (new Runnable () {
@Override
public void
run ()
{
view.setVisibility (View.VISIBLE);
if (!getDontFocusOnMap ())
/* Eventually this should check no-focus-on-map. */
view.requestFocus ();
}
});
}
}
public void
unmapWindow ()
{
if (!isMapped)
return;
isMapped = false;
view.post (new Runnable () {
@Override
public void
run ()
{
EmacsWindowAttachmentManager manager;
manager = EmacsWindowAttachmentManager.MANAGER;
view.setVisibility (View.GONE);
/* Detach the view from the window manager if possible. */
if (windowManager != null)
windowManager.removeView (view);
windowManager = null;
/* Now that the window is unmapped, unregister it as
well. */
manager.detachWindow (EmacsWindow.this);
}
});
}
@Override
public Canvas
lockCanvas ()
{
return view.getCanvas ();
}
@Override
public void
damageRect (Rect damageRect)
{
view.damageRect (damageRect);
}
public void
swapBuffers ()
{
view.swapBuffers ();
}
public void
clearWindow ()
{
synchronized (this)
{
EmacsService.SERVICE.fillRectangle (this, scratchGC,
0, 0, rect.width (),
rect.height ());
}
}
public void
clearArea (int x, int y, int width, int height)
{
EmacsService.SERVICE.fillRectangle (this, scratchGC,
x, y, width, height);
}
@Override
public Bitmap
getBitmap ()
{
return view.getBitmap ();
}
public void
onKeyDown (int keyCode, KeyEvent event)
{
int state;
state = event.getModifiers ();
state &= ~(KeyEvent.META_ALT_MASK | KeyEvent.META_CTRL_MASK);
EmacsNative.sendKeyPress (this.handle,
event.getEventTime (),
event.getModifiers (),
keyCode,
/* Ignore meta-state understood by Emacs
for now, or Ctrl+C will not be
recognized as an ASCII key press
event. */
event.getUnicodeChar (state));
lastModifiers = event.getModifiers ();
}
public void
onKeyUp (int keyCode, KeyEvent event)
{
int state;
state = event.getModifiers ();
state &= ~(KeyEvent.META_ALT_MASK | KeyEvent.META_CTRL_MASK);
EmacsNative.sendKeyRelease (this.handle,
event.getEventTime (),
event.getModifiers (),
keyCode,
event.getUnicodeChar (state));
lastModifiers = event.getModifiers ();
}
public void
onFocusChanged (boolean gainFocus)
{
EmacsActivity.invalidateFocus ();
}
public void
onActivityDetached ()
{
/* Destroy the associated frame when the activity is detached. */
EmacsNative.sendWindowAction (this.handle, 0);
}
/* Look through the button state to determine what button EVENT was
generated from. DOWN is true if EVENT is a button press event,
false otherwise. Value is the X number of the button. */
private int
whatButtonWasIt (MotionEvent event, boolean down)
{
int eventState, notIn;
if (Build.VERSION.SDK_INT
< Build.VERSION_CODES.ICE_CREAM_SANDWICH)
/* Earlier versions of Android only support one mouse
button. */
return 1;
eventState = event.getButtonState ();
notIn = (down ? eventState & ~lastButtonState
: lastButtonState & ~eventState);
if ((notIn & MotionEvent.BUTTON_PRIMARY) != 0)
return 1;
if ((notIn & MotionEvent.BUTTON_SECONDARY) != 0)
return 3;
if ((notIn & MotionEvent.BUTTON_TERTIARY) != 0)
return 2;
/* Not a real value. */
return 4;
}
/* Return the ID of the pointer which changed in EVENT. Value is -1
if it could not be determined, else the pointer that changed, or
-2 if -1 would have been returned, but there is also a pointer
that is a mouse. */
private int
figureChange (MotionEvent event)
{
int pointerID, i, truncatedX, truncatedY, pointerIndex;
Coordinate coordinate;
boolean mouseFlag;
/* pointerID is always initialized but the Java compiler is too
dumb to know that. */
pointerID = -1;
mouseFlag = false;
switch (event.getActionMasked ())
{
case MotionEvent.ACTION_DOWN:
/* Primary pointer pressed with index 0. */
/* Detect mice. If this is a mouse event, give it to
onSomeKindOfMotionEvent. */
if ((Build.VERSION.SDK_INT
>= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
&& event.getToolType (0) == MotionEvent.TOOL_TYPE_MOUSE)
return -2;
pointerID = event.getPointerId (0);
pointerMap.put (pointerID,
new Coordinate ((int) event.getX (0),
(int) event.getY (0)));
break;
case MotionEvent.ACTION_UP:
/* Primary pointer released with index 0. */
pointerID = event.getPointerId (0);
pointerMap.remove (pointerID);
break;
case MotionEvent.ACTION_POINTER_DOWN:
/* New pointer. Find the pointer ID from the index and place
it in the map. */
pointerIndex = event.getActionIndex ();
pointerID = event.getPointerId (pointerIndex);
pointerMap.put (pointerID,
new Coordinate ((int) event.getX (pointerID),
(int) event.getY (pointerID)));
break;
case MotionEvent.ACTION_POINTER_UP:
/* Pointer removed. Remove it from the map. */
pointerIndex = event.getActionIndex ();
pointerID = event.getPointerId (pointerIndex);
pointerMap.remove (pointerID);
break;
default:
/* Loop through each pointer in the event. */
for (i = 0; i < event.getPointerCount (); ++i)
{
pointerID = event.getPointerId (i);
/* Look up that pointer in the map. */
coordinate = pointerMap.get (pointerID);
if (coordinate != null)
{
/* See if coordinates have changed. */
truncatedX = (int) event.getX (i);
truncatedY = (int) event.getY (i);
if (truncatedX != coordinate.x
|| truncatedY != coordinate.y)
{
/* The pointer changed. Update the coordinate and
break out of the loop. */
coordinate.x = truncatedX;
coordinate.y = truncatedY;
break;
}
}
/* See if this is a mouse. If so, set the mouseFlag. */
if ((Build.VERSION.SDK_INT
>= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
&& event.getToolType (i) == MotionEvent.TOOL_TYPE_MOUSE)
mouseFlag = true;
}
/* Set the pointer ID to -1 if the loop failed to find any
changed pointer. If a mouse pointer was found, set it to
-2. */
if (i == event.getPointerCount ())
pointerID = (mouseFlag ? -2 : -1);
}
/* Return the pointer ID. */
return pointerID;
}
public boolean
onTouchEvent (MotionEvent event)
{
int pointerID, index;
/* Extract the ``touch ID'' (or in Android, the ``pointer
ID''.) */
pointerID = figureChange (event);
if (pointerID < 0)
{
/* If this is a mouse event, give it to
onSomeKindOfMotionEvent. */
if (pointerID == -2)
return onSomeKindOfMotionEvent (event);
return false;
}
/* Find the pointer index corresponding to the event. */
index = event.findPointerIndex (pointerID);
switch (event.getActionMasked ())
{
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
/* Touch down event. */
EmacsNative.sendTouchDown (this.handle, (int) event.getX (index),
(int) event.getY (index),
event.getEventTime (), pointerID);
return true;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
/* Touch up event. */
EmacsNative.sendTouchUp (this.handle, (int) event.getX (index),
(int) event.getY (index),
event.getEventTime (), pointerID);
return true;
case MotionEvent.ACTION_MOVE:
/* Pointer motion event. */
EmacsNative.sendTouchMove (this.handle, (int) event.getX (index),
(int) event.getY (index),
event.getEventTime (), pointerID);
return true;
}
return false;
}
public boolean
onSomeKindOfMotionEvent (MotionEvent event)
{
if (!event.isFromSource (InputDevice.SOURCE_CLASS_POINTER))
return false;
switch (event.getAction ())
{
case MotionEvent.ACTION_HOVER_ENTER:
EmacsNative.sendEnterNotify (this.handle, (int) event.getX (),
(int) event.getY (),
event.getEventTime ());
return true;
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_HOVER_MOVE:
EmacsNative.sendMotionNotify (this.handle, (int) event.getX (),
(int) event.getY (),
event.getEventTime ());
return true;
case MotionEvent.ACTION_HOVER_EXIT:
EmacsNative.sendLeaveNotify (this.handle, (int) event.getX (),
(int) event.getY (),
event.getEventTime ());
return true;
case MotionEvent.ACTION_BUTTON_PRESS:
/* Find the button which was pressed. */
EmacsNative.sendButtonPress (this.handle, (int) event.getX (),
(int) event.getY (),
event.getEventTime (),
lastModifiers,
whatButtonWasIt (event, true));
if (Build.VERSION.SDK_INT
< Build.VERSION_CODES.ICE_CREAM_SANDWICH)
return true;
lastButtonState = event.getButtonState ();
return true;
case MotionEvent.ACTION_BUTTON_RELEASE:
/* Find the button which was released. */
EmacsNative.sendButtonRelease (this.handle, (int) event.getX (),
(int) event.getY (),
event.getEventTime (),
lastModifiers,
whatButtonWasIt (event, false));
if (Build.VERSION.SDK_INT
< Build.VERSION_CODES.ICE_CREAM_SANDWICH)
return true;
lastButtonState = event.getButtonState ();
return true;
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_UP:
/* Emacs must return true even though touch events are not
handled here, because the value of this function is used by
the system to decide whether or not Emacs gets ACTION_MOVE
events. */
return true;
case MotionEvent.ACTION_SCROLL:
/* Send a scroll event with the specified deltas. */
EmacsNative.sendWheel (this.handle, (int) event.getX (),
(int) event.getY (),
event.getEventTime (),
lastModifiers,
event.getAxisValue (MotionEvent.AXIS_HSCROLL),
event.getAxisValue (MotionEvent.AXIS_VSCROLL));
return true;
}
return false;
}
public void
reparentTo (final EmacsWindow otherWindow, int x, int y)
{
int width, height;
/* Reparent this window to the other window. */
if (parent != null)
parent.children.remove (this);
if (otherWindow != null)
otherWindow.children.add (this);
parent = otherWindow;
/* Move this window to the new location. */
synchronized (this)
{
width = rect.width ();
height = rect.height ();
rect.left = x;
rect.top = y;
rect.right = x + width;
rect.bottom = y + height;
}
/* Now do the work necessary on the UI thread to reparent the
window. */
EmacsService.SERVICE.runOnUiThread (new Runnable () {
@Override
public void
run ()
{
EmacsWindowAttachmentManager manager;
ViewManager parent;
/* First, detach this window if necessary. */
manager = EmacsWindowAttachmentManager.MANAGER;
manager.detachWindow (EmacsWindow.this);
/* Also unparent this view. */
/* If the window manager is set, use that instead. */
if (windowManager != null)
parent = windowManager;
else
parent = (ViewManager) view.getParent ();
windowManager = null;
if (parent != null)
parent.removeView (view);
/* Next, either add this window as a child of the new
parent's view, or make it available again. */
if (otherWindow != null)
otherWindow.view.addView (view);
else if (EmacsWindow.this.isMapped)
manager.registerWindow (EmacsWindow.this);
/* Request relayout. */
view.requestLayout ();
}
});
}
public void
makeInputFocus (long time)
{
/* TIME is currently ignored. Request the input focus now. */
EmacsService.SERVICE.runOnUiThread (new Runnable () {
@Override
public void
run ()
{
view.requestFocus ();
}
});
}
public void
raise ()
{
/* This does nothing here. */
if (parent == null)
return;
/* Remove and add this view again. */
parent.children.remove (this);
parent.children.add (this);
/* Request a relayout. */
EmacsService.SERVICE.runOnUiThread (new Runnable () {
@Override
public void
run ()
{
view.raise ();
}
});
}
public void
lower ()
{
/* This does nothing here. */
if (parent == null)
return;
/* Remove and add this view again. */
parent.children.remove (this);
parent.children.add (this);
/* Request a relayout. */
EmacsService.SERVICE.runOnUiThread (new Runnable () {
@Override
public void
run ()
{
view.lower ();
}
});
}
public int[]
getWindowGeometry ()
{
int[] array;
Rect rect;
array = new int[4];
rect = getGeometry ();
array[0] = rect.left;
array[1] = rect.top;
array[2] = rect.width ();
array[3] = rect.height ();
return array;
}
public void
noticeIconified ()
{
isIconified = true;
EmacsNative.sendIconified (this.handle);
}
public void
noticeDeiconified ()
{
isIconified = false;
EmacsNative.sendDeiconified (this.handle);
}
public synchronized void
setDontAcceptFocus (final boolean dontAcceptFocus)
{
this.dontAcceptFocus = dontAcceptFocus;
/* Update the view's focus state. */
EmacsService.SERVICE.runOnUiThread (new Runnable () {
@Override
public void
run ()
{
view.setFocusable (!dontAcceptFocus);
view.setFocusableInTouchMode (!dontAcceptFocus);
}
});
}
public synchronized void
setDontFocusOnMap (final boolean dontFocusOnMap)
{
this.dontFocusOnMap = dontFocusOnMap;
}
public synchronized boolean
getDontFocusOnMap ()
{
return dontFocusOnMap;
}
public int[]
translateCoordinates (int x, int y)
{
int[] array;
/* This is supposed to translate coordinates to the root
window. */
array = new int[2];
EmacsService.SERVICE.getLocationOnScreen (view, array);
/* Now, the coordinates of the view should be in array. Offset X
and Y by them. */
array[0] += x;
array[1] += y;
/* Return the resulting coordinates. */
return array;
}
};