Introduce an input method callback required by Android 34
* java/org/gnu/emacs/EmacsInputConnection.java (replaceText): New function. * java/org/gnu/emacs/EmacsNative.java (replaceText): Declare native function. * src/androidgui.h (enum android_ime_operation): New operation ANDROID_IME_REPLACE_TEXT. * src/androidterm.c (android_handle_ime_event): Decode text when encountering an ANDROID_IME_REPLACE_TEXT operation. Return if decoding overflowed rather than presenting Qnil to textconv functions. (replaceText): New JNI function. * src/frame.h (enum text_conversion_operation): New operation TEXTCONV_REPLACE_TEXT. * src/textconv.c (really_commit_text): Move point to start if the composing region is set. (really_replace_text): New function. (handle_pending_conversion_events_1) <TEXTCONV_REPLACE_TEXT>: New case. (replace_text): New function. * src/textconv.h: Update prototypes.
This commit is contained in:
parent
253f1aff1a
commit
123b77436e
7 changed files with 238 additions and 1 deletions
|
@ -628,6 +628,21 @@ public final class EmacsInputConnection implements InputConnection
|
|||
batchEditCount = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean
|
||||
replaceText (int start, int end, CharSequence text,
|
||||
int newCursorPosition, TextAttribute attributes)
|
||||
{
|
||||
if (EmacsService.DEBUG_IC)
|
||||
Log.d (TAG, ("replaceText: " + text + ":: " + start + ","
|
||||
+ end + "," + newCursorPosition));
|
||||
|
||||
EmacsNative.replaceText (windowHandle, start, end,
|
||||
text.toString (), newCursorPosition,
|
||||
attributes);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
import android.view.inputmethod.ExtractedText;
|
||||
import android.view.inputmethod.ExtractedTextRequest;
|
||||
import android.view.inputmethod.SurroundingText;
|
||||
import android.view.inputmethod.TextAttribute;
|
||||
import android.view.inputmethod.TextSnapshot;
|
||||
|
||||
public final class EmacsNative
|
||||
|
@ -219,6 +220,9 @@ public static native void deleteSurroundingText (short window,
|
|||
int leftLength,
|
||||
int rightLength);
|
||||
public static native void finishComposingText (short window);
|
||||
public static native void replaceText (short window, int start, int end,
|
||||
String text, int newCursorPosition,
|
||||
TextAttribute attributes);
|
||||
public static native String getSelectedText (short window, int flags);
|
||||
public static native String getTextAfterCursor (short window, int length,
|
||||
int flags);
|
||||
|
|
|
@ -463,6 +463,7 @@ enum android_ime_operation
|
|||
ANDROID_IME_END_BATCH_EDIT,
|
||||
ANDROID_IME_REQUEST_SELECTION_UPDATE,
|
||||
ANDROID_IME_REQUEST_CURSOR_UPDATES,
|
||||
ANDROID_IME_REPLACE_TEXT,
|
||||
};
|
||||
|
||||
enum
|
||||
|
|
|
@ -687,9 +687,17 @@ android_handle_ime_event (union android_event *event, struct frame *f)
|
|||
{
|
||||
case ANDROID_IME_COMMIT_TEXT:
|
||||
case ANDROID_IME_SET_COMPOSING_TEXT:
|
||||
case ANDROID_IME_REPLACE_TEXT:
|
||||
text = android_decode_utf16 (event->ime.text,
|
||||
event->ime.length);
|
||||
xfree (event->ime.text);
|
||||
|
||||
/* Return should text be long enough that it overflows ptrdiff_t.
|
||||
Such circumstances are detected within android_decode_utf16. */
|
||||
|
||||
if (NILP (text))
|
||||
return;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -773,6 +781,12 @@ android_handle_ime_event (union android_event *event, struct frame *f)
|
|||
case ANDROID_IME_REQUEST_CURSOR_UPDATES:
|
||||
android_request_cursor_updates (f, event->ime.length);
|
||||
break;
|
||||
|
||||
case ANDROID_IME_REPLACE_TEXT:
|
||||
replace_text (f, event->ime.start, event->ime.end,
|
||||
text, event->ime.position,
|
||||
event->ime.counter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4856,6 +4870,39 @@ NATIVE_NAME (finishComposingText) (JNIEnv *env, jobject object,
|
|||
android_write_event (&event);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
NATIVE_NAME (replaceText) (JNIEnv *env, jobject object, jshort window,
|
||||
jint start, jint end, jobject text,
|
||||
int new_cursor_position, jobject attribute)
|
||||
{
|
||||
JNI_STACK_ALIGNMENT_PROLOGUE;
|
||||
|
||||
union android_event event;
|
||||
size_t length;
|
||||
|
||||
/* First, obtain a copy of the Java string. */
|
||||
text = android_copy_java_string (env, text, &length);
|
||||
|
||||
if (!text)
|
||||
return;
|
||||
|
||||
/* Next, populate the event with the information in this function's
|
||||
arguments. */
|
||||
|
||||
event.ime.type = ANDROID_INPUT_METHOD;
|
||||
event.ime.serial = ++event_serial;
|
||||
event.ime.window = window;
|
||||
event.ime.operation = ANDROID_IME_REPLACE_TEXT;
|
||||
event.ime.start = start + 1;
|
||||
event.ime.end = end + 1;
|
||||
event.ime.length = length;
|
||||
event.ime.position = new_cursor_position;
|
||||
event.ime.text = text;
|
||||
event.ime.counter = ++edit_counter;
|
||||
|
||||
android_write_event (&event);
|
||||
}
|
||||
|
||||
/* Structure describing the context used for a text query. */
|
||||
|
||||
struct android_conversion_query_context
|
||||
|
|
|
@ -90,6 +90,7 @@ enum text_conversion_operation
|
|||
TEXTCONV_DELETE_SURROUNDING_TEXT,
|
||||
TEXTCONV_REQUEST_POINT_UPDATE,
|
||||
TEXTCONV_BARRIER,
|
||||
TEXTCONV_REPLACE_TEXT,
|
||||
};
|
||||
|
||||
/* Structure describing a single edit being performed by the input
|
||||
|
|
168
src/textconv.c
168
src/textconv.c
|
@ -616,6 +616,12 @@ really_commit_text (struct frame *f, EMACS_INT position,
|
|||
end = max (mark, PT);
|
||||
}
|
||||
|
||||
/* If it transpires that the start of the compose region is not
|
||||
point, move point there. */
|
||||
|
||||
if (start != PT)
|
||||
set_point (start);
|
||||
|
||||
/* Now delete whatever needs to go. */
|
||||
|
||||
del_range_1 (start, end, true, false);
|
||||
|
@ -635,7 +641,7 @@ really_commit_text (struct frame *f, EMACS_INT position,
|
|||
record_buffer_change (start, PT, text);
|
||||
}
|
||||
|
||||
/* Move to a the position specified in POSITION. */
|
||||
/* Move to the position specified in POSITION. */
|
||||
|
||||
if (position <= 0)
|
||||
{
|
||||
|
@ -1154,6 +1160,135 @@ really_set_point_and_mark (struct frame *f, ptrdiff_t point,
|
|||
unbind_to (count, Qnil);
|
||||
}
|
||||
|
||||
/* Remove the composing region. Replace the text between START and
|
||||
END in F's selected window with TEXT, then set point to POSITION
|
||||
relative to it. If the mark is active, deactivate it. */
|
||||
|
||||
static void
|
||||
really_replace_text (struct frame *f, ptrdiff_t start, ptrdiff_t end,
|
||||
Lisp_Object text, ptrdiff_t position)
|
||||
{
|
||||
specpdl_ref count;
|
||||
ptrdiff_t new_start, new_end, wanted;
|
||||
struct window *w;
|
||||
|
||||
/* If F's old selected window is no longer alive, fail. */
|
||||
|
||||
if (!WINDOW_LIVE_P (f->old_selected_window))
|
||||
return;
|
||||
|
||||
count = SPECPDL_INDEX ();
|
||||
record_unwind_protect (restore_selected_window,
|
||||
selected_window);
|
||||
|
||||
/* Make the composition region markers point elsewhere. */
|
||||
|
||||
if (!NILP (f->conversion.compose_region_start))
|
||||
{
|
||||
Fset_marker (f->conversion.compose_region_start, Qnil, Qnil);
|
||||
Fset_marker (f->conversion.compose_region_end, Qnil, Qnil);
|
||||
f->conversion.compose_region_start = Qnil;
|
||||
f->conversion.compose_region_end = Qnil;
|
||||
|
||||
/* Notify the IME of an update to the composition region,
|
||||
inasmuch as the point might not change if START and END are
|
||||
identical and TEXT is empty, among other circumstances. */
|
||||
|
||||
if (text_interface
|
||||
&& text_interface->compose_region_changed)
|
||||
(*text_interface->compose_region_changed) (f);
|
||||
}
|
||||
|
||||
/* Delete the composition region overlay. */
|
||||
|
||||
if (!NILP (f->conversion.compose_region_overlay))
|
||||
Fdelete_overlay (f->conversion.compose_region_overlay);
|
||||
|
||||
/* Temporarily switch to F's selected window at the time of the last
|
||||
redisplay. */
|
||||
select_window (f->old_selected_window, Qt);
|
||||
|
||||
/* Sort START and END by magnitude. */
|
||||
new_start = min (start, end);
|
||||
new_end = max (start, end);
|
||||
|
||||
/* Now constrain both to the accessible region. */
|
||||
|
||||
if (new_start < BEGV)
|
||||
new_start = BEGV;
|
||||
else if (new_start > ZV)
|
||||
new_start = ZV;
|
||||
|
||||
if (new_end < BEGV)
|
||||
new_end = BEGV;
|
||||
else if (new_end > ZV)
|
||||
new_end = ZV;
|
||||
|
||||
start = new_start;
|
||||
end = new_end;
|
||||
|
||||
/* This should deactivate the mark. */
|
||||
call0 (Qdeactivate_mark);
|
||||
|
||||
/* Go to start. */
|
||||
set_point (start);
|
||||
|
||||
/* Now delete the text in between, and save PT before TEXT is
|
||||
inserted. */
|
||||
del_range_1 (start, end, true, false);
|
||||
record_buffer_change (start, start, Qt);
|
||||
wanted = PT;
|
||||
|
||||
/* So long as TEXT isn't empty, insert it now. */
|
||||
|
||||
if (SCHARS (text))
|
||||
{
|
||||
/* Insert the new text. Make sure to inherit text properties
|
||||
from the surroundings: if this doesn't happen, CC Mode
|
||||
fontification might grow confused and become very slow. */
|
||||
|
||||
insert_from_string (text, 0, 0, SCHARS (text),
|
||||
SBYTES (text), true);
|
||||
record_buffer_change (start, PT, text);
|
||||
}
|
||||
|
||||
/* Now, move point to the position designated by POSITION. */
|
||||
|
||||
if (position <= 0)
|
||||
{
|
||||
if (INT_ADD_WRAPV (wanted, position, &wanted)
|
||||
|| wanted < BEGV)
|
||||
wanted = BEGV;
|
||||
|
||||
if (wanted > ZV)
|
||||
wanted = ZV;
|
||||
|
||||
set_point (wanted);
|
||||
}
|
||||
else
|
||||
{
|
||||
wanted = PT;
|
||||
|
||||
if (INT_ADD_WRAPV (wanted, position - 1, &wanted)
|
||||
|| wanted > ZV)
|
||||
wanted = ZV;
|
||||
|
||||
if (wanted < BEGV)
|
||||
wanted = BEGV;
|
||||
|
||||
set_point (wanted);
|
||||
}
|
||||
|
||||
/* Print some debugging information. */
|
||||
TEXTCONV_DEBUG ("text inserted: %s, point now: %zd",
|
||||
SSDATA (text), PT);
|
||||
|
||||
/* Update the ephemeral last point. */
|
||||
w = XWINDOW (selected_window);
|
||||
w->ephemeral_last_point = PT;
|
||||
unbind_to (count, Qnil);
|
||||
}
|
||||
|
||||
/* Complete the edit specified by the counter value inside *TOKEN. */
|
||||
|
||||
static void
|
||||
|
@ -1325,6 +1460,13 @@ handle_pending_conversion_events_1 (struct frame *f,
|
|||
if (w)
|
||||
w->ephemeral_last_point = window_point (w);
|
||||
break;
|
||||
|
||||
case TEXTCONV_REPLACE_TEXT:
|
||||
really_replace_text (f, XFIXNUM (XCAR (data)),
|
||||
XFIXNUM (XCAR (XCDR (data))),
|
||||
XCAR (XCDR (XCDR (data))),
|
||||
XFIXNUM (XCAR (XCDR (XCDR (XCDR (data))))));
|
||||
break;
|
||||
}
|
||||
|
||||
/* Signal success. */
|
||||
|
@ -1679,6 +1821,30 @@ textconv_barrier (struct frame *f, unsigned long counter)
|
|||
input_pending = true;
|
||||
}
|
||||
|
||||
/* Remove the composing region. Replace the text between START and
|
||||
END within F's selected window with TEXT; deactivate the mark if it
|
||||
is active. Subsequently, set point to POSITION relative to TEXT,
|
||||
much as `commit_text' would. */
|
||||
|
||||
void
|
||||
replace_text (struct frame *f, ptrdiff_t start, ptrdiff_t end,
|
||||
Lisp_Object text, ptrdiff_t position,
|
||||
unsigned long counter)
|
||||
{
|
||||
struct text_conversion_action *action, **last;
|
||||
|
||||
action = xmalloc (sizeof *action);
|
||||
action->operation = TEXTCONV_REPLACE_TEXT;
|
||||
action->data = list4 (make_fixnum (start), make_fixnum (end),
|
||||
text, make_fixnum (position));
|
||||
action->next = NULL;
|
||||
action->counter = counter;
|
||||
for (last = &f->conversion.actions; *last; last = &(*last)->next)
|
||||
;;
|
||||
*last = action;
|
||||
input_pending = true;
|
||||
}
|
||||
|
||||
/* Return N characters of text around point in frame F's old selected
|
||||
window.
|
||||
|
||||
|
|
|
@ -142,6 +142,9 @@ extern void delete_surrounding_text (struct frame *, ptrdiff_t,
|
|||
ptrdiff_t, unsigned long);
|
||||
extern void request_point_update (struct frame *, unsigned long);
|
||||
extern void textconv_barrier (struct frame *, unsigned long);
|
||||
extern void replace_text (struct frame *, ptrdiff_t, ptrdiff_t,
|
||||
Lisp_Object, ptrdiff_t, unsigned long);
|
||||
|
||||
extern char *get_extracted_text (struct frame *, ptrdiff_t, ptrdiff_t *,
|
||||
ptrdiff_t *, ptrdiff_t *, ptrdiff_t *,
|
||||
ptrdiff_t *, bool *);
|
||||
|
|
Loading…
Add table
Reference in a new issue