This source file includes following definitions.
- popup_activated
- android_init_emacs_context_menu
- android_unwind_local_frame
- android_push_local_frame
- android_dismiss_menu
- android_process_events_for_menu
- android_free_subprefixes
- android_menu_show
- android_init_emacs_dialog
- android_dialog_show
- android_popup_dialog
- popup_activated
- DEFUN
- init_androidmenu
- syms_of_androidmenu
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 #include <config.h>
21
22 #include "lisp.h"
23 #include "androidterm.h"
24 #include "android.h"
25 #include "blockinput.h"
26 #include "keyboard.h"
27 #include "menu.h"
28
29 #ifndef ANDROID_STUBIFY
30
31 #include <android/log.h>
32
33
34
35
36 static int popup_activated_flag;
37
38
39
40
41 unsigned int current_menu_serial;
42
43 int
44 popup_activated (void)
45 {
46 return popup_activated_flag;
47 }
48
49
50
51
52
53
54
55 struct android_emacs_context_menu
56 {
57 jclass class;
58 jmethodID create_context_menu;
59 jmethodID add_item;
60 jmethodID add_submenu;
61 jmethodID add_pane;
62 jmethodID parent;
63 jmethodID display;
64 jmethodID dismiss;
65 };
66
67
68 static struct android_emacs_context_menu menu_class;
69
70 static void
71 android_init_emacs_context_menu (void)
72 {
73 jclass old;
74
75 menu_class.class
76 = (*android_java_env)->FindClass (android_java_env,
77 "org/gnu/emacs/EmacsContextMenu");
78 eassert (menu_class.class);
79
80 old = menu_class.class;
81 menu_class.class
82 = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
83 (jobject) old);
84 ANDROID_DELETE_LOCAL_REF (old);
85
86 if (!menu_class.class)
87 emacs_abort ();
88
89 #define FIND_METHOD(c_name, name, signature) \
90 menu_class.c_name \
91 = (*android_java_env)->GetMethodID (android_java_env, \
92 menu_class.class, \
93 name, signature); \
94 eassert (menu_class.c_name);
95
96 #define FIND_METHOD_STATIC(c_name, name, signature) \
97 menu_class.c_name \
98 = (*android_java_env)->GetStaticMethodID (android_java_env, \
99 menu_class.class, \
100 name, signature); \
101 eassert (menu_class.c_name);
102
103 FIND_METHOD_STATIC (create_context_menu, "createContextMenu",
104 "(Ljava/lang/String;)"
105 "Lorg/gnu/emacs/EmacsContextMenu;");
106
107 FIND_METHOD (add_item, "addItem", "(ILjava/lang/String;ZZZ"
108 "Ljava/lang/String;Z)V");
109 FIND_METHOD (add_submenu, "addSubmenu", "(Ljava/lang/String;"
110 "Ljava/lang/String;)"
111 "Lorg/gnu/emacs/EmacsContextMenu;");
112 FIND_METHOD (add_pane, "addPane", "(Ljava/lang/String;)V");
113 FIND_METHOD (parent, "parent", "()Lorg/gnu/emacs/EmacsContextMenu;");
114 FIND_METHOD (display, "display", "(Lorg/gnu/emacs/EmacsWindow;III)Z");
115 FIND_METHOD (dismiss, "dismiss", "(Lorg/gnu/emacs/EmacsWindow;)V");
116
117 #undef FIND_METHOD
118 #undef FIND_METHOD_STATIC
119 }
120
121 static void
122 android_unwind_local_frame (void)
123 {
124 (*android_java_env)->PopLocalFrame (android_java_env, NULL);
125 }
126
127
128
129
130
131 static void
132 android_push_local_frame (void)
133 {
134 int rc;
135
136 rc = (*android_java_env)->PushLocalFrame (android_java_env, 30);
137
138
139 if (rc < 1)
140 android_exception_check ();
141
142 record_unwind_protect_void (android_unwind_local_frame);
143 }
144
145
146
147 struct android_dismiss_menu_data
148 {
149
150 jobject menu;
151
152
153 jobject window;
154 };
155
156
157
158
159 static void
160 android_dismiss_menu (void *pointer)
161 {
162 struct android_dismiss_menu_data *data;
163
164 data = pointer;
165 (*android_java_env)->CallVoidMethod (android_java_env,
166 data->menu,
167 menu_class.dismiss,
168 data->window);
169 popup_activated_flag = 0;
170 }
171
172
173
174
175
176 static void
177 android_process_events_for_menu (int *id)
178 {
179 int blocked;
180
181
182
183
184 x_display_list->menu_event_id = -1;
185
186
187 blocked = interrupt_input_blocked;
188 totally_unblock_input ();
189
190
191 while (x_display_list->menu_event_id == -1)
192 {
193
194 android_wait_event ();
195
196
197 process_pending_signals ();
198
199
200
201
202
203 maybe_quit ();
204 }
205
206
207 interrupt_input_blocked = blocked;
208
209
210 *id = x_display_list->menu_event_id;
211 }
212
213
214
215 struct android_menu_subprefix
216 {
217
218 struct android_menu_subprefix *last;
219
220
221 Lisp_Object subprefix;
222 };
223
224
225
226 static void
227 android_free_subprefixes (void *data)
228 {
229 struct android_menu_subprefix **head, *subprefix;
230
231 head = data;
232
233 while (*head)
234 {
235 subprefix = *head;
236 *head = subprefix->last;
237
238 xfree (subprefix);
239 }
240 }
241
242 Lisp_Object
243 android_menu_show (struct frame *f, int x, int y, int menuflags,
244 Lisp_Object title, const char **error_name)
245 {
246 jobject context_menu, current_context_menu;
247 jobject title_string, help_string, temp;
248 size_t i;
249 Lisp_Object pane_name, prefix;
250 const char *pane_string;
251 specpdl_ref count, count1;
252 Lisp_Object item_name, enable, def, tem, entry, type, selected;
253 Lisp_Object help;
254 jmethodID method;
255 jobject store;
256 bool rc;
257 jobject window;
258 int id, item_id, submenu_depth;
259 struct android_dismiss_menu_data data;
260 struct android_menu_subprefix *subprefix, *temp_subprefix;
261 struct android_menu_subprefix *subprefix_1;
262 bool checkmark;
263 unsigned int serial;
264
265 count = SPECPDL_INDEX ();
266 serial = ++current_menu_serial;
267
268 block_input ();
269
270
271 android_push_local_frame ();
272
273
274
275
276
277 title_string = NULL;
278 if (STRINGP (title) && menu_items_n_panes < 2)
279 title_string = android_build_string (title);
280
281
282 method = menu_class.create_context_menu;
283 current_context_menu = context_menu
284 = (*android_java_env)->CallStaticObjectMethod (android_java_env,
285 menu_class.class,
286 method,
287 title_string);
288
289
290
291 if (title_string)
292 ANDROID_DELETE_LOCAL_REF (title_string);
293
294
295 count1 = SPECPDL_INDEX ();
296 android_push_local_frame ();
297
298
299 i = 0, submenu_depth = 0;
300
301 while (i < menu_items_used)
302 {
303 if (NILP (AREF (menu_items, i)))
304 {
305
306
307 i += 1;
308 submenu_depth += 1;
309 }
310 else if (EQ (AREF (menu_items, i), Qlambda))
311 {
312
313
314 store = current_context_menu;
315 current_context_menu
316 = (*android_java_env)->CallObjectMethod (android_java_env,
317 current_context_menu,
318 menu_class.parent);
319 android_exception_check ();
320
321 if (store != context_menu)
322 ANDROID_DELETE_LOCAL_REF (store);
323 i += 1;
324 submenu_depth -= 1;
325
326 if (!current_context_menu || submenu_depth < 0)
327 {
328 __android_log_print (ANDROID_LOG_FATAL, __func__,
329 "unbalanced submenu pop in menu_items");
330 emacs_abort ();
331 }
332 }
333 else if (EQ (AREF (menu_items, i), Qt)
334 && submenu_depth != 0)
335 i += MENU_ITEMS_PANE_LENGTH;
336 else if (EQ (AREF (menu_items, i), Qquote))
337 i += 1;
338 else if (EQ (AREF (menu_items, i), Qt))
339 {
340
341
342
343
344 if (menu_items_n_panes < 2)
345 goto next_item;
346
347
348
349 if (current_context_menu != context_menu)
350 ANDROID_DELETE_LOCAL_REF (current_context_menu);
351 current_context_menu = context_menu;
352
353
354 pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME);
355 prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
356 pane_string = (NILP (pane_name)
357 ? "" : SSDATA (pane_name));
358 if ((menuflags & MENU_KEYMAPS) && !NILP (prefix))
359 pane_string++;
360
361
362 temp = (*android_java_env)->NewStringUTF (android_java_env,
363 pane_string);
364 android_exception_check ();
365
366 (*android_java_env)->CallVoidMethod (android_java_env,
367 current_context_menu,
368 menu_class.add_pane,
369 temp);
370 android_exception_check ();
371 ANDROID_DELETE_LOCAL_REF (temp);
372
373 next_item:
374 i += MENU_ITEMS_PANE_LENGTH;
375 }
376 else
377 {
378 item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
379 enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
380 def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION);
381 type = AREF (menu_items, i + MENU_ITEMS_ITEM_TYPE);
382 selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED);
383 help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP);
384
385
386
387
388 if (i + MENU_ITEMS_ITEM_LENGTH < menu_items_used
389 && NILP (AREF (menu_items, i + MENU_ITEMS_ITEM_LENGTH)))
390 {
391
392 title_string = (!NILP (item_name)
393 ? android_build_string (item_name)
394 : NULL);
395 help_string = NULL;
396
397
398
399
400 if (android_get_current_api_level () >= 26
401 && STRINGP (help))
402 help_string = android_build_string (help);
403
404 store = current_context_menu;
405 current_context_menu
406 = (*android_java_env)->CallObjectMethod (android_java_env,
407 current_context_menu,
408 menu_class.add_submenu,
409 title_string,
410 help_string);
411 android_exception_check ();
412
413 if (store != context_menu)
414 ANDROID_DELETE_LOCAL_REF (store);
415
416 if (title_string)
417 ANDROID_DELETE_LOCAL_REF (title_string);
418
419 if (help_string)
420 ANDROID_DELETE_LOCAL_REF (help_string);
421 }
422 else if (NILP (def) && menu_separator_name_p (SSDATA (item_name)))
423
424 ;
425 else
426 {
427
428
429
430 if (!INT_ADD_OK (0, i + MENU_ITEMS_ITEM_VALUE, &item_id))
431 memory_full (i + MENU_ITEMS_ITEM_VALUE * sizeof (Lisp_Object));
432
433
434
435 title_string = (!NILP (item_name)
436 ? android_build_string (item_name)
437 : NULL);
438 help_string = NULL;
439
440
441
442
443 if (android_get_current_api_level () >= 26
444 && STRINGP (help))
445 help_string = android_build_string (help);
446
447
448
449 checkmark = (EQ (type, QCtoggle)
450 || EQ (type, QCradio));
451
452 (*android_java_env)->CallVoidMethod (android_java_env,
453 current_context_menu,
454 menu_class.add_item,
455 (jint) item_id,
456 title_string,
457 (jboolean) !NILP (enable),
458 (jboolean) checkmark,
459 (jboolean) !NILP (selected),
460 help_string,
461 (jboolean) (EQ (type,
462 QCradio)));
463 android_exception_check ();
464
465 if (title_string)
466 ANDROID_DELETE_LOCAL_REF (title_string);
467
468 if (help_string)
469 ANDROID_DELETE_LOCAL_REF (help_string);
470 }
471
472 i += MENU_ITEMS_ITEM_LENGTH;
473 }
474 }
475
476
477 unbind_to (count1, Qnil);
478
479
480 window = android_resolve_handle (FRAME_ANDROID_WINDOW (f),
481 ANDROID_HANDLE_WINDOW);
482 rc = (*android_java_env)->CallBooleanMethod (android_java_env,
483 context_menu,
484 menu_class.display,
485 window, (jint) x,
486 (jint) y,
487 (jint) serial);
488 android_exception_check ();
489
490 if (!rc)
491
492 goto finish;
493
494
495 data.menu = context_menu;
496 data.window = window;
497 record_unwind_protect_ptr (android_dismiss_menu, &data);
498
499
500 popup_activated_flag = 1;
501 android_process_events_for_menu (&id);
502
503 if (!id)
504
505 goto finish;
506
507
508 if (id >= ASIZE (menu_items))
509 goto finish;
510
511
512 tem = Qnil;
513 subprefix = NULL;
514 record_unwind_protect_ptr (android_free_subprefixes, &subprefix);
515
516
517
518
519 prefix = entry = Qnil;
520 i = 0;
521 while (i < menu_items_used)
522 {
523 if (NILP (AREF (menu_items, i)))
524 {
525 temp_subprefix = xmalloc (sizeof *temp_subprefix);
526 temp_subprefix->last = subprefix;
527 subprefix = temp_subprefix;
528 subprefix->subprefix = prefix;
529
530 prefix = entry;
531 i++;
532 }
533 else if (EQ (AREF (menu_items, i), Qlambda))
534 {
535 prefix = subprefix->subprefix;
536 temp_subprefix = subprefix->last;
537 xfree (subprefix);
538 subprefix = temp_subprefix;
539
540 i++;
541 }
542 else if (EQ (AREF (menu_items, i), Qt))
543 {
544 prefix
545 = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
546 i += MENU_ITEMS_PANE_LENGTH;
547 }
548
549
550 else if (EQ (AREF (menu_items, i), Qquote))
551 i += 1;
552 else
553 {
554 entry = AREF (menu_items, i + MENU_ITEMS_ITEM_VALUE);
555
556 if (i + MENU_ITEMS_ITEM_VALUE == id)
557 {
558 if (menuflags & MENU_KEYMAPS)
559 {
560 entry = list1 (entry);
561
562 if (!NILP (prefix))
563 entry = Fcons (prefix, entry);
564
565 for (subprefix_1 = subprefix; subprefix_1;
566 subprefix_1 = subprefix_1->last)
567 if (!NILP (subprefix_1->subprefix))
568 entry = Fcons (subprefix_1->subprefix, entry);
569 }
570
571 tem = entry;
572 }
573 i += MENU_ITEMS_ITEM_LENGTH;
574 }
575 }
576
577 unblock_input ();
578 return unbind_to (count, tem);
579
580 finish:
581 unblock_input ();
582 return unbind_to (count, Qnil);
583 }
584
585
586
587
588
589
590
591 struct android_emacs_dialog
592 {
593 jclass class;
594 jmethodID create_dialog;
595 jmethodID add_button;
596 jmethodID display;
597 };
598
599
600 static struct android_emacs_dialog dialog_class;
601
602 static void
603 android_init_emacs_dialog (void)
604 {
605 jclass old;
606
607 dialog_class.class
608 = (*android_java_env)->FindClass (android_java_env,
609 "org/gnu/emacs/EmacsDialog");
610 eassert (dialog_class.class);
611
612 old = dialog_class.class;
613 dialog_class.class
614 = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
615 (jobject) old);
616 ANDROID_DELETE_LOCAL_REF (old);
617
618 if (!dialog_class.class)
619 emacs_abort ();
620
621 #define FIND_METHOD(c_name, name, signature) \
622 dialog_class.c_name \
623 = (*android_java_env)->GetMethodID (android_java_env, \
624 dialog_class.class, \
625 name, signature); \
626 eassert (dialog_class.c_name);
627
628 #define FIND_METHOD_STATIC(c_name, name, signature) \
629 dialog_class.c_name \
630 = (*android_java_env)->GetStaticMethodID (android_java_env, \
631 dialog_class.class, \
632 name, signature); \
633
634 FIND_METHOD_STATIC (create_dialog, "createDialog", "(Ljava/lang/String;"
635 "Ljava/lang/String;I)Lorg/gnu/emacs/EmacsDialog;");
636 FIND_METHOD (add_button, "addButton", "(Ljava/lang/String;IZ)V");
637 FIND_METHOD (display, "display", "()Z");
638
639 #undef FIND_METHOD
640 #undef FIND_METHOD_STATIC
641 }
642
643 static Lisp_Object
644 android_dialog_show (struct frame *f, Lisp_Object title,
645 Lisp_Object header, const char **error_name)
646 {
647 specpdl_ref count;
648 jobject dialog, java_header, java_title, temp;
649 size_t i;
650 Lisp_Object item_name, enable, entry;
651 bool rc;
652 int id;
653 jmethodID method;
654 unsigned int serial;
655
656
657 serial = ++current_menu_serial;
658
659 if (menu_items_n_panes > 1)
660 {
661 *error_name = "Multiple panes in dialog box";
662 return Qnil;
663 }
664
665
666 count = SPECPDL_INDEX ();
667 *error_name = NULL;
668
669 android_push_local_frame ();
670
671
672 java_header = (!NILP (header)
673 ? android_build_jstring ("Information")
674 : android_build_jstring ("Question"));
675
676
677 java_title = android_build_string (title);
678
679
680 method = dialog_class.create_dialog;
681 dialog = (*android_java_env)->CallStaticObjectMethod (android_java_env,
682 dialog_class.class,
683 method, java_header,
684 java_title,
685 (jint) serial);
686 android_exception_check ();
687
688
689 if (java_header)
690 ANDROID_DELETE_LOCAL_REF (java_header);
691 ANDROID_DELETE_LOCAL_REF (java_title);
692
693
694 i = MENU_ITEMS_PANE_LENGTH;
695 while (i < menu_items_used)
696 {
697 item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
698 enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
699
700
701
702 if (NILP (item_name))
703 {
704 *error_name = "Submenu in dialog items";
705 return unbind_to (count, Qnil);
706 }
707
708
709
710
711
712 if (EQ (item_name, Qquote))
713 ++i;
714 else
715 {
716
717 if (i > TYPE_MAXIMUM (jint))
718 {
719 *error_name = "Dialog box too big";
720 return unbind_to (count, Qnil);
721 }
722
723
724 temp = android_build_string (item_name);
725 (*android_java_env)->CallVoidMethod (android_java_env,
726 dialog,
727 dialog_class.add_button,
728 temp, (jint) i,
729 (jboolean) NILP (enable));
730 android_exception_check ();
731 ANDROID_DELETE_LOCAL_REF (temp);
732 i += MENU_ITEMS_ITEM_LENGTH;
733 }
734 }
735
736
737 rc = (*android_java_env)->CallBooleanMethod (android_java_env,
738 dialog,
739 dialog_class.display);
740 android_exception_check ();
741
742 if (!rc)
743 quit ();
744
745
746 android_process_events_for_menu (&id);
747
748 if (!id)
749 quit ();
750
751
752
753 i = 0;
754 while (i < menu_items_used)
755 {
756 if (EQ (AREF (menu_items, i), Qt))
757 i += MENU_ITEMS_PANE_LENGTH;
758 else if (EQ (AREF (menu_items, i), Qquote))
759
760
761 ++i;
762 else
763 {
764 entry = AREF (menu_items, i + MENU_ITEMS_ITEM_VALUE);
765
766 if (id == i)
767 return entry;
768
769 i += MENU_ITEMS_ITEM_LENGTH;
770 }
771 }
772
773 return Qnil;
774 }
775
776 Lisp_Object
777 android_popup_dialog (struct frame *f, Lisp_Object header,
778 Lisp_Object contents)
779 {
780 Lisp_Object title;
781 const char *error_name;
782 Lisp_Object selection;
783 specpdl_ref specpdl_count = SPECPDL_INDEX ();
784
785 check_window_system (f);
786
787
788 title = Fcar (contents);
789 CHECK_STRING (title);
790 record_unwind_protect_void (unuse_menu_items);
791
792 if (NILP (Fcar (Fcdr (contents))))
793
794
795 contents = list2 (title, Fcons (build_string ("Ok"), Qt));
796
797 list_of_panes (list1 (contents));
798
799
800 block_input ();
801 selection = android_dialog_show (f, title, header, &error_name);
802 unblock_input ();
803
804 unbind_to (specpdl_count, Qnil);
805 discard_menu_items ();
806
807 if (error_name)
808 error ("%s", error_name);
809
810 return selection;
811 }
812
813 #else
814
815 int
816 popup_activated (void)
817 {
818 return 0;
819 }
820
821 #endif
822
823 DEFUN ("menu-or-popup-active-p", Fmenu_or_popup_active_p,
824 Smenu_or_popup_active_p, 0, 0, 0,
825 doc: )
826 (void)
827 {
828 return (popup_activated ()) ? Qt : Qnil;
829 }
830
831 void
832 init_androidmenu (void)
833 {
834 #ifndef ANDROID_STUBIFY
835 android_init_emacs_context_menu ();
836 android_init_emacs_dialog ();
837 #endif
838 }
839
840 void
841 syms_of_androidmenu (void)
842 {
843 defsubr (&Smenu_or_popup_active_p);
844 }