/* 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;
}