/* Haiku window system support Copyright (C) 2021 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 . */ #include #include "lisp.h" #include "keyboard.h" #include "menu.h" #include "buffer.h" #include "blockinput.h" #include "haikuterm.h" #include "haiku_support.h" static Lisp_Object *volatile menu_item_selection; int popup_activated_p = 0; struct submenu_stack_cell { void *parent_menu; void *pane; }; static void digest_menu_items (void *first_menu, int start, int menu_items_used, int mbar_p) { void **menus, **panes; ssize_t menu_len = (menu_items_used + 1 - start) * sizeof *menus; ssize_t pane_len = (menu_items_used + 1 - start) * sizeof *panes; menus = alloca (menu_len); panes = alloca (pane_len); int i = start, menu_depth = 0; memset (menus, 0, menu_len); memset (panes, 0, pane_len); void *menu = first_menu; menus[0] = first_menu; while (i < menu_items_used) { if (NILP (AREF (menu_items, i))) { menus[++menu_depth] = menu; i++; } else if (EQ (AREF (menu_items, i), Qlambda)) { panes[menu_depth] = NULL; menu = panes[--menu_depth] ? panes[menu_depth] : menus[menu_depth]; i++; } else if (EQ (AREF (menu_items, i), Qquote)) i += 1; else if (EQ (AREF (menu_items, i), Qt)) { Lisp_Object pane_name, prefix; const char *pane_string; if (menu_items_n_panes == 1) { i += MENU_ITEMS_PANE_LENGTH; continue; } pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME); prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX); if (STRINGP (pane_name) && STRING_MULTIBYTE (pane_name)) { pane_name = ENCODE_UTF_8 (pane_name); ASET (menu_items, i + MENU_ITEMS_PANE_NAME, pane_name); } pane_string = (NILP (pane_name) ? "" : SSDATA (pane_name)); if (!NILP (prefix)) pane_string++; if (strcmp (pane_string, "")) { panes[menu_depth] = menu = BMenu_new_submenu (menus[menu_depth], pane_string, 1); } i += MENU_ITEMS_PANE_LENGTH; } else { Lisp_Object item_name, enable, descrip, def, selected; item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME); enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE); descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY); def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION); selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED); if (STRINGP (item_name) && STRING_MULTIBYTE (item_name)) { item_name = ENCODE_UTF_8 (item_name); ASET (menu_items, i + MENU_ITEMS_ITEM_NAME, item_name); } if (STRINGP (descrip) && STRING_MULTIBYTE (descrip)) { descrip = ENCODE_UTF_8 (descrip); ASET (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY, descrip); } if (i + MENU_ITEMS_ITEM_LENGTH < menu_items_used && NILP (AREF (menu_items, i + MENU_ITEMS_ITEM_LENGTH))) menu = BMenu_new_submenu (menu, SSDATA (item_name), !NILP (enable)); else if (NILP (def) && menu_separator_name_p (SSDATA (item_name))) BMenu_add_separator (menu); else if (!mbar_p) BMenu_add_item (menu, SSDATA (item_name), !NILP (def) ? aref_addr (menu_items, i) : NULL, !NILP (enable), !NILP (selected)); else BMenu_add_item (menu, SSDATA (item_name), !NILP (def) ? (void *) (intptr_t) i : NULL, !NILP (enable), !NILP (selected)); i += MENU_ITEMS_ITEM_LENGTH; } } } static Lisp_Object haiku_dialog_show (struct frame *f, Lisp_Object title, Lisp_Object header, const char **error_name) { int i, nb_buttons = 0; *error_name = NULL; if (menu_items_n_panes > 1) { *error_name = "Multiple panes in dialog box"; return Qnil; } Lisp_Object pane_name = AREF (menu_items, MENU_ITEMS_PANE_NAME); i = MENU_ITEMS_PANE_LENGTH; if (STRING_MULTIBYTE (pane_name)) pane_name = ENCODE_UTF_8 (pane_name); block_input (); void *alert = BAlert_new (SSDATA (pane_name), NILP (header) ? HAIKU_INFO_ALERT : HAIKU_IDEA_ALERT); Lisp_Object vals[10]; while (i < menu_items_used) { Lisp_Object item_name, enable, descrip, value; item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME); enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE); descrip = AREF (menu_items, i + MENU_ITEMS_ITEM_EQUIV_KEY); value = AREF (menu_items, i + MENU_ITEMS_ITEM_VALUE); if (NILP (item_name)) { BAlert_delete (alert); *error_name = "Submenu in dialog items"; unblock_input (); return Qnil; } if (EQ (item_name, Qquote)) { i++; } if (nb_buttons >= 9) { BAlert_delete (alert); *error_name = "Too many dialog items"; unblock_input (); return Qnil; } if (STRING_MULTIBYTE (item_name)) item_name = ENCODE_UTF_8 (item_name); if (!NILP (descrip) && STRING_MULTIBYTE (descrip)) descrip = ENCODE_UTF_8 (descrip); void *button = BAlert_add_button (alert, SSDATA (item_name)); BButton_set_enabled (button, !NILP (enable)); if (!NILP (descrip)) BView_set_tooltip (button, SSDATA (descrip)); vals[nb_buttons] = value; ++nb_buttons; i += MENU_ITEMS_ITEM_LENGTH; } int32_t val = BAlert_go (alert); unblock_input (); if (val < 0) quit (); else return vals[val]; return Qnil; } Lisp_Object haiku_popup_dialog (struct frame *f, Lisp_Object header, Lisp_Object contents) { Lisp_Object title; const char *error_name = NULL; Lisp_Object selection; ptrdiff_t specpdl_count = SPECPDL_INDEX (); check_window_system (f); /* Decode the dialog items from what was specified. */ title = Fcar (contents); CHECK_STRING (title); record_unwind_protect_void (unuse_menu_items); if (NILP (Fcar (Fcdr (contents)))) /* No buttons specified, add an "Ok" button so users can pop down the dialog. Also, the lesstif/motif version crashes if there are no buttons. */ contents = list2 (title, Fcons (build_string ("Ok"), Qt)); list_of_panes (list1 (contents)); /* Display them in a dialog box. */ block_input (); selection = haiku_dialog_show (f, title, header, &error_name); unblock_input (); unbind_to (specpdl_count, Qnil); discard_menu_items (); if (error_name) error ("%s", error_name); return selection; } Lisp_Object haiku_menu_show (struct frame *f, int x, int y, int menuflags, Lisp_Object title, const char **error_name) { int i = 0, submenu_depth = 0; void *view = FRAME_HAIKU_VIEW (f); void *menu; Lisp_Object *subprefix_stack = alloca (menu_items_used * sizeof (Lisp_Object)); eassert (FRAME_HAIKU_P (f)); *error_name = NULL; if (menu_items_used <= MENU_ITEMS_PANE_LENGTH) { *error_name = "Empty menu"; return Qnil; } block_input (); if (STRINGP (title) && STRING_MULTIBYTE (title)) title = ENCODE_UTF_8 (title); menu = BPopUpMenu_new (STRINGP (title) ? SSDATA (title) : NULL); digest_menu_items (menu, 0, menu_items_used, 0); BView_convert_to_screen (view, &x, &y); menu_item_selection = BMenu_run (menu, x, y); if (menu_item_selection) { Lisp_Object prefix, entry; prefix = entry = Qnil; i = 0; while (i < menu_items_used) { if (NILP (AREF (menu_items, i))) { subprefix_stack[submenu_depth++] = prefix; prefix = entry; i++; } else if (EQ (AREF (menu_items, i), Qlambda)) { prefix = subprefix_stack[--submenu_depth]; i++; } else if (EQ (AREF (menu_items, i), Qt)) { prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX); i += MENU_ITEMS_PANE_LENGTH; } /* Ignore a nil in the item list. It's meaningful only for dialog boxes. */ else if (EQ (AREF (menu_items, i), Qquote)) i += 1; else { entry = AREF (menu_items, i + MENU_ITEMS_ITEM_VALUE); if (menu_item_selection == aref_addr (menu_items, i)) { if (menuflags & MENU_KEYMAPS) { int j; entry = list1 (entry); if (!NILP (prefix)) entry = Fcons (prefix, entry); for (j = submenu_depth - 1; j >= 0; j--) if (!NILP (subprefix_stack[j])) entry = Fcons (subprefix_stack[j], entry); } BPopUpMenu_delete (menu); unblock_input (); return entry; } i += MENU_ITEMS_ITEM_LENGTH; } } } else if (!(menuflags & MENU_FOR_CLICK)) { BPopUpMenu_delete (menu); unblock_input (); quit (); } BPopUpMenu_delete (menu); unblock_input (); return Qnil; } void free_frame_menubar (struct frame *f) { FRAME_MENU_BAR_LINES (f) = 0; FRAME_MENU_BAR_HEIGHT (f) = 0; FRAME_EXTERNAL_MENU_BAR (f) = 0; block_input (); void *mbar = FRAME_HAIKU_MENU_BAR (f); if (mbar) BMenuBar_delete (mbar); unblock_input (); adjust_frame_size (f, -1, -1, 2, false, Qmenu_bar_lines); } void initialize_frame_menubar (struct frame *f) { /* This function is called before the first chance to redisplay the frame. It has to be, so the frame will have the right size. */ fset_menu_bar_items (f, menu_bar_items (FRAME_MENU_BAR_ITEMS (f))); set_frame_menubar (f, true); } void set_frame_menubar (struct frame *f, bool deep_p) { void *mbar = FRAME_HAIKU_MENU_BAR (f); void *view = FRAME_HAIKU_VIEW (f); int first_time_p = 0; if (!mbar) { mbar = FRAME_HAIKU_MENU_BAR (f) = BMenuBar_new (view); first_time_p = 1; } Lisp_Object items; struct buffer *prev = current_buffer; Lisp_Object buffer; ptrdiff_t specpdl_count = SPECPDL_INDEX (); int previous_menu_items_used = f->menu_bar_items_used; Lisp_Object *previous_items = alloca (previous_menu_items_used * sizeof *previous_items); XSETFRAME (Vmenu_updating_frame, f); if (!deep_p) { FRAME_OUTPUT_DATA (f)->menu_up_to_date_p = 0; items = FRAME_MENU_BAR_ITEMS (f); Lisp_Object string; block_input (); int count = BMenu_count_items (mbar); int i; for (i = 0; i < ASIZE (items); i += 4) { string = AREF (items, i + 1); if (!STRINGP (string)) break; if (STRING_MULTIBYTE (string)) string = ENCODE_UTF_8 (string); if (i / 4 < count) { void *it = BMenu_item_at (mbar, i / 4); BMenu_item_set_label (it, SSDATA (string)); } else BMenu_new_menu_bar_submenu (mbar, SSDATA (string)); } if (i / 4 < count) BMenu_delete_from (mbar, i / 4, count - i / 4 + 1); unblock_input (); f->menu_bar_items_used = 0; } else { /* If we are making a new widget, its contents are empty, do always reinitialize them. */ if (first_time_p) previous_menu_items_used = 0; buffer = XWINDOW (FRAME_SELECTED_WINDOW (f))->contents; specbind (Qinhibit_quit, Qt); /* Don't let the debugger step into this code because it is not reentrant. */ specbind (Qdebug_on_next_call, Qnil); record_unwind_save_match_data (); if (NILP (Voverriding_local_map_menu_flag)) { specbind (Qoverriding_terminal_local_map, Qnil); specbind (Qoverriding_local_map, Qnil); } set_buffer_internal_1 (XBUFFER (buffer)); /* Run the Lucid hook. */ safe_run_hooks (Qactivate_menubar_hook); /* If it has changed current-menubar from previous value, really recompute the menubar from the value. */ if (! NILP (Vlucid_menu_bar_dirty_flag)) call0 (Qrecompute_lucid_menubar); safe_run_hooks (Qmenu_bar_update_hook); fset_menu_bar_items (f, menu_bar_items (FRAME_MENU_BAR_ITEMS (f))); items = FRAME_MENU_BAR_ITEMS (f); /* Save the frame's previous menu bar contents data. */ if (previous_menu_items_used) memcpy (previous_items, xvector_contents (f->menu_bar_vector), previous_menu_items_used * word_size); /* Fill in menu_items with the current menu bar contents. This can evaluate Lisp code. */ save_menu_items (); menu_items = f->menu_bar_vector; menu_items_allocated = VECTORP (menu_items) ? ASIZE (menu_items) : 0; init_menu_items (); int i; int count = BMenu_count_items (mbar); int subitems = ASIZE (items) / 4; int *submenu_start, *submenu_end, *submenu_n_panes; Lisp_Object *submenu_names; submenu_start = alloca ((subitems + 1) * sizeof *submenu_start); submenu_end = alloca (subitems * sizeof *submenu_end); submenu_n_panes = alloca (subitems * sizeof *submenu_n_panes); submenu_names = alloca (subitems * sizeof (Lisp_Object)); for (i = 0; i < subitems; ++i) { Lisp_Object key, string, maps; key = AREF (items, i * 4); string = AREF (items, i * 4 + 1); maps = AREF (items, i * 4 + 2); if (NILP (string)) break; if (STRINGP (string) && STRING_MULTIBYTE (string)) string = ENCODE_UTF_8 (string); submenu_start[i] = menu_items_used; menu_items_n_panes = 0; parse_single_submenu (key, string, maps); submenu_n_panes[i] = menu_items_n_panes; submenu_end[i] = menu_items_used; submenu_names[i] = string; } finish_menu_items (); submenu_start[i] = -1; block_input (); for (i = 0; submenu_start[i] >= 0; ++i) { void *mn = NULL; if (i < count) mn = BMenu_item_get_menu (BMenu_item_at (mbar, i)); if (mn) BMenu_delete_all (mn); else mn = BMenu_new_menu_bar_submenu (mbar, SSDATA (submenu_names[i])); menu_items_n_panes = submenu_n_panes[i]; digest_menu_items (mn, submenu_start[i], submenu_end[i], 1); } unblock_input (); set_buffer_internal_1 (prev); FRAME_OUTPUT_DATA (f)->menu_up_to_date_p = 1; fset_menu_bar_vector (f, menu_items); f->menu_bar_items_used = menu_items_used; } unbind_to (specpdl_count, Qnil); } DEFUN ("menu-or-popup-active-p", Fmenu_or_popup_active_p, Smenu_or_popup_active_p, 0, 0, 0, doc: /* SKIP: real doc in xmenu.c. */) (void) { return popup_activated_p ? Qt : Qnil; } void syms_of_haikumenu (void) { DEFSYM (Qdebug_on_next_call, "debug-on-next-call"); defsubr (&Smenu_or_popup_active_p); return; }