2 * Copyright Neil Brown ©2023 <neil@brown.name>
3 * May be distributed under terms of GPLv2 - see file:COPYING
5 * trim a line off the top line of a pane and place a menu
6 * bar. Actions are sent to the focus.
8 * We place a renderline at the top and construct a string
9 * to give to it as needed.
10 * We create menu documents as children of the main pane,
11 * and display them as needed.
13 * Menus are added either to LHS or RHS and must be added in order
14 * So FILE EDIT VIEW must be in order, and HELP on right before
15 * any other right-side menus.
16 * Before displayed a menu, the pane which requested it is given
17 * a chance to update the content via a "menu:refresh" notification.
19 * Menus are created and populated with "menubar-add" which acts
20 * like menu-add, The name is "X/Y" where is the name of the menu
21 * and Y is the name in the menu. If X doesn't exist, the menu
22 * is created. If Y already exists, the other details are updated.
23 * menubar-delete and menubar-clear can delete individual menus,
24 * or clear all entries so they can be repopulated.
26 * Menu documents are collected as children of this pane. The focus
27 * of each document is the pane which requested the window. This
28 * allows the menu to be discarded when that pane is closed, and to
29 * be hidden when the pane loses focus.
31 * Child panes have ->z value:
33 * 1 or more for active menu
34 * -1 for menu documents created by in-focus clients
35 * -2 for menu documents created by not-in-focus clients
39 #define PANE_DATA_TYPE struct mbinfo
45 struct pane *menu, *open;
48 #include "core-pane.h"
50 static struct map *menubar_map;
51 DEF_LOOKUP_CMD(menubar_handle, menubar_map);
53 DEF_CMD(menubar_border)
55 struct mbinfo *mbi = ci->home->data;
57 mbi->hidden = !mbi->wanted || ci->num <= 0;
58 pane_damaged(ci->home, DAMAGED_SIZE);
62 DEF_CMD(menubar_refresh_size)
64 struct mbinfo *mbi = ci->home->data;
65 struct pane *p = mbi->bar;
67 if (!p || mbi->hidden) {
68 /* Put bar below window - out of sight */
70 pane_resize(p, 0, ci->home->h,
73 pane_resize(mbi->child, 0, 0,
74 ci->home->w, ci->home->h);
76 pane_resize(p, 0, 0, ci->home->w, ci->home->h/3);
77 pane_damaged(p, DAMAGED_REFRESH);
78 call("render-line:measure", p, -1);
79 if (mbi->child && ci->home->h > p->h)
80 pane_resize(mbi->child, 0, p->h,
81 ci->home->w, ci->home->h - p->h);
83 pane_damaged(ci->home, DAMAGED_VIEW);
87 DEF_CMD(menubar_child_notify)
89 struct mbinfo *mbi = ci->home->data;
95 if (ci->home->focus == ci->focus)
96 ci->home->focus = NULL;
100 pane_close(mbi->child);
101 mbi->child = ci->focus;
102 ci->home->focus = ci->focus;
107 DEF_CMD(menubar_refresh)
110 struct pane *home = ci->home;
111 struct mbinfo *mbi = home->data;
113 struct pane *bar = mbi->bar;
116 if (mbi->hidden || !bar)
121 buf_concat(&b, ACK SOH "tab:20" STX);
123 list_for_each_entry(p, &home->children, siblings) {
128 /* Strange - every doc should have a focus... */
132 if (!pane_has_focus(p->focus, mbi->child))
133 /* Owner of this menu not in focus */
135 n = pane_attr_get(p, "doc-name");
138 for (c = n ; *c; c++)
139 if (*c == ',' || (*c > 0 && *c < ' '))
141 if (mbi->menu && mbi->open == p)
142 buf_concat(&b, SOH "fg:black,bg:white-80,"
145 buf_concat(&b, SOH "fg:blue,underline,"
150 buf_concat(&b, ETX " ");
156 call("render-line:set", bar, -1, NULL, buf_final(&b),
157 0, NULL, "bg:#ffa500+50");
158 pane_resize(bar, 0, 0, bar->w, home->h/3);
159 call("render-line:measure", bar, -1);
161 pane_damaged(home, DAMAGED_SIZE);
171 static struct pane *menubar_find(struct pane *home safe,
173 const char *name safe,
174 enum create_where create)
176 struct pane *p, *m, *d;
178 struct pane *last_left = NULL;
180 list_for_each_entry(p, &home->children, siblings) {
184 /* Strange - every doc should have a focus... */
186 /* If no owner, then we only want currently visible docs */
187 if (!owner && p->z != -1)
189 a = pane_attr_get(p, "doc-name");
190 if (owner && p->focus != owner)
192 if (!a || strcmp(name, a) != 0)
196 if (create == C_NOWHERE || !owner)
198 m = call_ret(pane, "attach-menu", home, 0, NULL, "DV", 0, NULL,
202 d = call_ret(pane, "doc:get-doc", m);
204 call("doc:set:autoclose", d, 0);
205 call("popup:close", m);
208 call("doc:set-name", d, 0, NULL, name);
209 call("doc:set:menubar-side", d, 0, NULL,
210 create == C_LEFT ? "left" : "right");
211 /* Find insertion point */
212 list_for_each_entry(p, &home->children, siblings) {
216 /* Strange - every doc should have a focus... */
218 a = pane_attr_get(p, "menubar-side");
219 if (a && strcmp(a, "left") == 0)
223 pane_reparent(d, home);
225 pane_add_notify(home, owner, "Notify:Close");
227 list_move(&d->siblings, &last_left->siblings);
228 pane_damaged(home, DAMAGED_VIEW);
238 if (!ci->str || !ci->str2)
241 val = strchr(ci->str, '/');
244 menu = strndup(ci->str, val - ci->str);
246 d = menubar_find(ci->home, ci->focus, menu,
247 ci->num & 2 ? C_RIGHT : C_LEFT);
252 call("menu:add", d, 0, NULL, val, 0, NULL, ci->str2);
256 DEF_CMD(menubar_delete)
262 d = menubar_find(ci->home, ci->focus, ci->str, C_NOWHERE);
269 DEF_CMD(menubar_clear)
275 d = menubar_find(ci->home, ci->focus, ci->str, C_NOWHERE);
278 call("menu:clear", d);
282 DEF_CMD(menubar_done)
284 struct pane *home = ci->home;
285 struct mbinfo *mbi = home->data;
288 pane_take_focus(mbi->child);
289 if (!ci->str || !ci->str[0])
291 if (ci->str[0] == ' ')
292 call(ci->str+1, pane_focus(home));
294 call("Keystroke-sequence", home, 0, NULL, ci->str);
298 DEF_CMD(menubar_root)
300 /* Provide a pane for popup to attach to */
301 comm_call(ci->comm2, "cb", ci->home);
305 DEF_CMD(menubar_view_changed)
307 //struct mbinfo *mbi = ci->home->data;
309 //ci->home->focus = mbi->child;
313 DEF_CMD(menubar_press)
315 struct mbinfo *mbi = ci->home->data;
316 struct call_return cr;
321 if (ci->focus != mbi->bar || !mbi->bar)
324 call("popup:close", mbi->menu);
326 pane_damaged(ci->home, DAMAGED_VIEW);
328 cih = pane_mapxy(mbi->bar, ci->home,
329 ci->x == INT_MAX ? ci->focus->cx : ci->x,
330 ci->y == INT_MAX ? ci->focus->cy : ci->y,
332 cr = pane_call_ret(all, mbi->bar, "render-line:findxy",
333 mbi->bar, -1, NULL, NULL,
338 if (cr.s && sscanf(cr.s, "%dx%d,", &x, &y) == 2) {
342 list_for_each_entry(p, &ci->home->children, siblings) {
347 if (p->x < cr.ret - 1)
350 /* FIXME this should be pane_call, but emacs mode is
353 home_call(p->focus, "menu:refresh", p);
354 mbi->menu = call_ret(pane, "attach-menu", p, 0, NULL, "DVF",
358 pane_add_notify(ci->home, mbi->menu, "Notify:Close");
361 pane_damaged(ci->home, DAMAGED_VIEW);
367 DEF_CMD(menubar_release)
369 struct mbinfo *mbi = ci->home->data;
370 struct pane *c = pane_my_child(ci->home, ci->focus);
375 /* any button maps to -3 for menu action */
376 return home_call(ci->home->parent, "M:Release-3", ci->focus,
377 ci->num, ci->mark, ci->str,
378 ci->num2, ci->mark2, ci->str2,
379 ci->x, ci->y, ci->comm2);
382 DEF_CMD(menubar_close_notify)
384 struct mbinfo *mbi = ci->home->data;
387 if (ci->focus == mbi->menu) {
389 pane_damaged(ci->home, DAMAGED_VIEW);
392 if (ci->focus == mbi->child) {
396 if (ci->focus == mbi->bar) {
400 list_for_each_entry(p, &ci->home->children, siblings) {
403 if (p->focus == ci->focus) {
412 DEF_CMD(menubar_attach)
414 struct pane *ret, *mbp;
416 char *v = pane_attr_get(ci->focus, "menubar-visible");
418 ret = pane_register(ci->focus, 0, &menubar_handle.c);
423 if (v && strcmp(v, "no") == 0)
425 mbi->hidden = ! mbi->wanted;
426 mbp = call_ret(pane, "attach-renderline", ret, 1);
432 pane_add_notify(ret, mbp, "Notify:Close");
433 pane_damaged(ret, DAMAGED_VIEW);
434 comm_call(ci->comm2, "callback:attach", ret);
435 /* Allow pane close to root to register */
436 call("menubar:ready", ret);
440 void edlib_init(struct pane *ed safe)
442 call_comm("global-set-command", ed, &menubar_attach,
443 0, NULL, "attach-menubar");
445 menubar_map = key_alloc();
446 key_add(menubar_map, "Window:border", &menubar_border);
447 key_add(menubar_map, "Refresh:size", &menubar_refresh_size);
448 key_add(menubar_map, "Child-Notify", &menubar_child_notify);
449 key_add(menubar_map, "Refresh:view", &menubar_refresh);
450 key_add(menubar_map, "menubar-add", &menubar_add);
451 key_add(menubar_map, "menubar-delete", &menubar_delete);
452 key_add(menubar_map, "menubar-clear", &menubar_clear);
453 key_add(menubar_map, "menubar-done", &menubar_done);
454 key_add(menubar_map, "RootPane", &menubar_root);
455 key_add(menubar_map, "Notify:Close", &menubar_close_notify);
456 key_add(menubar_map, "view:changed", &menubar_view_changed);
457 key_add_prefix(menubar_map, "M:Press-", &menubar_press);
458 key_add_prefix(menubar_map, "M:Release-", &menubar_release);