diff options
| author | Po Lu <luangruo@yahoo.com> | 2023-01-17 22:10:43 +0800 |
|---|---|---|
| committer | Po Lu <luangruo@yahoo.com> | 2023-01-17 22:10:43 +0800 |
| commit | 1b8258a1f2b6a080a4f0e819aa4a86c1ec2da89f (patch) | |
| tree | d6c709e513882f5d430a98508e631cc503469fab /java/org/gnu/emacs/EmacsDialog.java | |
| parent | 356249d9faf2b454879ff30f06d97beb97fb9a36 (diff) | |
Update Android port
* doc/emacs/android.texi (Android Fonts): Document that TTC
format fonts are now supported.
* doc/emacs/emacs.texi (Top): Fix menus.
* doc/lispref/commands.texi (Touchscreen Events)
(Key Sequence Input): Document changes to touchscreen events.
* etc/DEBUG: Describe how to debug 64 bit binaries on Android.
* java/org/gnu/emacs/EmacsCopyArea.java (perform): Explicitly
recycle copy bitmap.
* java/org/gnu/emacs/EmacsDialog.java (EmacsDialog): New class.
* java/org/gnu/emacs/EmacsDrawRectangle.java (perform): Use 5
point PolyLine like X, because Android behaves like Postscript
on some devices and X elsewhere.
* java/org/gnu/emacs/EmacsFillRectangle.java (perform):
Explicitly recycle copy bitmap.
* java/org/gnu/emacs/EmacsPixmap.java (destroyHandle):
Explicitly recycle bitmap and GC if it is big.
* java/org/gnu/emacs/EmacsView.java (EmacsView): Make
`bitmapDirty' a boolean.
(handleDirtyBitmap): Reimplement in terms of that boolean.
Explicitly recycle old bitmap and GC.
(onLayout): Fix lock up.
(onDetachedFromWindow): Recycle bitmap and GC.
* java/org/gnu/emacs/EmacsWindow.java (requestViewLayout):
Update call to explicitlyDirtyBitmap.
* src/android.c (android_run_select_thread, android_select):
Really fix android_select.
(android_build_jstring): New function.
* src/android.h: Update prototypes.
* src/androidmenu.c (android_process_events_for_menu): Totally
unblock input before process_pending_signals.
(android_menu_show): Remove redundant unblock_input and
debugging code.
(struct android_emacs_dialog, android_init_emacs_dialog)
(android_dialog_show, android_popup_dialog, init_androidmenu):
Implement popup dialogs on Android.
* src/androidterm.c (android_update_tools)
(handle_one_android_event, android_frame_up_to_date): Allow
tapping tool bar items.
(android_create_terminal): Add dialog hook.
(android_wait_for_event): Adjust call to android_select.
* src/androidterm.h (struct android_touch_point): New field
`tool_bar_p'.
* src/keyboard.c (read_key_sequence, head_table)
(syms_of_keyboard): Prefix touchscreen events with posn.
* src/keyboard.h (EVENT_HEAD): Handle touchscreen events.
* src/process.c (wait_reading_process_output): Adjust call to
android_select.
* src/sfnt.c (sfnt_read_table_directory): If the first long
turns out to be ttcf, return -1.
(sfnt_read_ttc_header): New function.
(main): Test TTC support.
* src/sfnt.h (struct sfnt_ttc_header): New structure.
(enum sfnt_ttc_tag): New enum.
* src/sfntfont-android.c (struct
sfntfont_android_scanline_buffer): New structure.
(GET_SCANLINE_BUFFER): New macro. Try to avoid so much malloc
upon accessing the scanline buffer.
(sfntfont_android_put_glyphs): Do not use SAFE_ALLOCA to
allocate the scaline buffer.
(Fandroid_enumerate_fonts): Enumerate ttc fonts too.
* src/sfntfont.c (struct sfnt_font_desc): New field `offset'.
(sfnt_enum_font_1): Split out enumeration code from
sfnt_enum_font.
(sfnt_enum_font): Read TTC tables and enumerate each font
therein.
(sfntfont_open): Seek to the offset specified.
* xcompile/Makefile.in (maintainer-clean): Fix depends here.
Diffstat (limited to 'java/org/gnu/emacs/EmacsDialog.java')
| -rw-r--r-- | java/org/gnu/emacs/EmacsDialog.java | 333 |
1 files changed, 333 insertions, 0 deletions
diff --git a/java/org/gnu/emacs/EmacsDialog.java b/java/org/gnu/emacs/EmacsDialog.java new file mode 100644 index 00000000000..5bc8efa5978 --- /dev/null +++ b/java/org/gnu/emacs/EmacsDialog.java @@ -0,0 +1,333 @@ +/* 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.util.List; +import java.util.ArrayList; + +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.content.Context; +import android.util.Log; + +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.FrameLayout; + +import android.view.View; +import android.view.ViewGroup; + +/* Toolkit dialog implementation. This object is built from JNI and + describes a single alert dialog. Then, `inflate' turns it into + AlertDialog. */ + +public class EmacsDialog implements DialogInterface.OnDismissListener +{ + private static final String TAG = "EmacsDialog"; + + /* List of buttons in this dialog. */ + private List<EmacsButton> buttons; + + /* Dialog title. */ + private String title; + + /* Dialog text. */ + private String text; + + /* Whether or not a selection has already been made. */ + private boolean wasButtonClicked; + + /* Dialog to dismiss after click. */ + private AlertDialog dismissDialog; + + private class EmacsButton implements View.OnClickListener, + DialogInterface.OnClickListener + { + /* Name of this button. */ + public String name; + + /* ID of this button. */ + public int id; + + /* Whether or not the button is enabled. */ + public boolean enabled; + + @Override + public void + onClick (View view) + { + Log.d (TAG, "onClicked " + this); + + wasButtonClicked = true; + EmacsNative.sendContextMenu ((short) 0, id); + dismissDialog.dismiss (); + } + + @Override + public void + onClick (DialogInterface dialog, int which) + { + Log.d (TAG, "onClicked " + this); + + wasButtonClicked = true; + EmacsNative.sendContextMenu ((short) 0, id); + } + }; + + /* Create a popup dialog with the title TITLE and the text TEXT. + TITLE may be NULL. */ + + public static EmacsDialog + createDialog (String title, String text) + { + EmacsDialog dialog; + + dialog = new EmacsDialog (); + dialog.buttons = new ArrayList<EmacsButton> (); + dialog.title = title; + dialog.text = text; + + return dialog; + } + + /* Add a button named NAME, with the identifier ID. If DISABLE, + disable the button. */ + + public void + addButton (String name, int id, boolean disable) + { + EmacsButton button; + + button = new EmacsButton (); + button.name = name; + button.id = id; + button.enabled = !disable; + buttons.add (button); + } + + /* Turn this dialog into an AlertDialog for the specified + CONTEXT. + + Upon a button being selected, the dialog will send an + ANDROID_CONTEXT_MENU event with the id of that button. + + Upon the dialog being dismissed, an ANDROID_CONTEXT_MENU event + will be sent with an id of 0. */ + + public AlertDialog + toAlertDialog (Context context) + { + AlertDialog dialog; + int size; + EmacsButton button; + LinearLayout layout; + Button buttonView; + ViewGroup.LayoutParams layoutParams; + + size = buttons.size (); + + if (size <= 3) + { + dialog = new AlertDialog.Builder (context).create (); + dialog.setMessage (text); + dialog.setCancelable (true); + dialog.setOnDismissListener (this); + + if (title != null) + dialog.setTitle (title); + + /* There are less than 4 buttons. Add the buttons the way + Android intends them to be added. */ + + if (size >= 1) + { + button = buttons.get (0); + dialog.setButton (DialogInterface.BUTTON_POSITIVE, + button.name, button); + } + + if (size >= 2) + { + button = buttons.get (1); + dialog.setButton (DialogInterface.BUTTON_NEUTRAL, + button.name, button); + buttonView + = dialog.getButton (DialogInterface.BUTTON_NEUTRAL); + buttonView.setEnabled (button.enabled); + } + + if (size >= 3) + { + button = buttons.get (2); + dialog.setButton (DialogInterface.BUTTON_NEGATIVE, + button.name, button); + } + } + else + { + /* There are more than 4 buttons. Add them all to a + LinearLayout. */ + layout = new LinearLayout (context); + layoutParams + = new LinearLayout.LayoutParams (ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT); + + for (EmacsButton emacsButton : buttons) + { + buttonView = new Button (context); + buttonView.setText (emacsButton.name); + buttonView.setOnClickListener (emacsButton); + buttonView.setLayoutParams (layoutParams); + buttonView.setEnabled (emacsButton.enabled); + layout.addView (buttonView); + } + + layoutParams + = new FrameLayout.LayoutParams (ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT); + layout.setLayoutParams (layoutParams); + + /* Add that layout to the dialog's custom view. + + android.R.id.custom is documented to work. But looking it + up returns NULL, so setView must be used instead. */ + + dialog = new AlertDialog.Builder (context).setView (layout).create (); + dialog.setMessage (text); + dialog.setCancelable (true); + dialog.setOnDismissListener (this); + + if (title != null) + dialog.setTitle (title); + } + + return dialog; + } + + /* Internal helper for display run on the main thread. */ + + private boolean + display1 () + { + EmacsActivity activity; + int size; + Button buttonView; + EmacsButton button; + AlertDialog dialog; + + if (EmacsActivity.focusedActivities.isEmpty ()) + return false; + + activity = EmacsActivity.focusedActivities.get (0); + dialog = dismissDialog = toAlertDialog (activity); + dismissDialog.show (); + + /* If there are less than four buttons, then they must be + individually enabled or disabled after the dialog is + displayed. */ + size = buttons.size (); + + if (size <= 3) + { + if (size >= 1) + { + button = buttons.get (0); + buttonView + = dialog.getButton (DialogInterface.BUTTON_POSITIVE); + buttonView.setEnabled (button.enabled); + } + + if (size >= 2) + { + button = buttons.get (1); + dialog.setButton (DialogInterface.BUTTON_NEUTRAL, + button.name, button); + buttonView + = dialog.getButton (DialogInterface.BUTTON_NEUTRAL); + buttonView.setEnabled (button.enabled); + } + + if (size >= 3) + { + button = buttons.get (2); + buttonView + = dialog.getButton (DialogInterface.BUTTON_NEGATIVE); + buttonView.setEnabled (button.enabled); + } + } + + return true; + } + + /* Display this dialog for a suitable activity. + Value is false if the dialog could not be displayed, + and true otherwise. */ + + public boolean + display () + { + Runnable runnable; + final Holder<Boolean> rc; + + rc = new Holder<Boolean> (); + runnable = new Runnable () { + @Override + public void + run () + { + synchronized (this) + { + rc.thing = display1 (); + notify (); + } + } + }; + + synchronized (runnable) + { + EmacsService.SERVICE.runOnUiThread (runnable); + + try + { + runnable.wait (); + } + catch (InterruptedException e) + { + EmacsNative.emacsAbort (); + } + } + + return rc.thing; + } + + + + @Override + public void + onDismiss (DialogInterface dialog) + { + Log.d (TAG, "onDismiss: " + this); + + if (wasButtonClicked) + return; + + EmacsNative.sendContextMenu ((short) 0, 0); + } +}; |
