]> git.eshelyaron.com Git - emacs.git/commitdiff
Introduce an input method callback required by Android 34
authorPo Lu <luangruo@yahoo.com>
Thu, 5 Oct 2023 06:23:20 +0000 (14:23 +0800)
committerPo Lu <luangruo@yahoo.com>
Thu, 5 Oct 2023 06:23:20 +0000 (14:23 +0800)
* 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.

java/org/gnu/emacs/EmacsInputConnection.java
java/org/gnu/emacs/EmacsNative.java
src/androidgui.h
src/androidterm.c
src/frame.h
src/textconv.c
src/textconv.h

index c3764a7b29f85cc0f1a13bb5f46be6a2ef3e197e..7f6331205cbf61b2aa41e4bc2e7bfec804a30726 100644 (file)
@@ -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;
+  }
+
 \f
 
   public void
index a4b45aafbc14e2324945a8b5eab445a82a41aed9..d8524d92130011ca98a5b1554a84e5b772e94f3f 100644 (file)
@@ -26,6 +26,7 @@ import android.graphics.Bitmap;
 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 final class EmacsNative
                                                   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);
index 14225f7bf803e35c8b2281933f9489c458a2e996..936706b092e61c2830709635c52e3ffb480c7900 100644 (file)
@@ -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
index 438f8ce1fbb68565bd67ea30e8df2a58914914f9..9b00ad856424c1d60697fc41f77a9f4ae287c5cb 100644 (file)
@@ -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
index f4726f1c0e53be042b00edf45d9078234971d6d0..d826ae56e8b84f83d1b4937e85b00eb00c2c729b 100644 (file)
@@ -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
index 57daa7e53b6805ba4a207fcac1c85b2d2ec30661..bd72562317f6d43578377bd6185c1a038546f08d 100644 (file)
@@ -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 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.
 
index feac5b805af3ff34b6a6df3d945465d46e4658d9..c677c07e9aae3382b38dc4e7a1cf48ede24eeec3 100644 (file)
@@ -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 *);