2 * Copyright Neil Brown ©2015-2023 <neil@brown.name>
3 * May be distributed under terms of GPLv2 - see file:COPYING
5 * trim a line off the bottom of a pane and capture messages
6 * to go there. They disappear on the next keystroke.
8 * Later it might be good to allow boarderless popups to appear here.
10 * The message displayed is:
11 * a 'modal' message until a keystroke, or
12 * a normal message which remains until it has been visible without a modal for
13 * seven seconds with keystrokes, or 30 seconds without keystrokes, or
14 * a 'default' message ... which is hardly used, or
17 * Refreshed about every 15 seconds, so timestamp can be a little out of date
26 #define PANE_DATA_TYPE struct mlinfo
31 char *modal; /* message displays a mode, and must
32 * remain exactly until a keystroke
34 struct pane *line safe, *child;
37 time_t last_message; /* message should stay for at least 10 seconds */
39 #include "core-pane.h"
41 static struct pane *do_messageline_attach(struct pane *p safe);
42 static struct map *messageline_map;
43 DEF_LOOKUP_CMD(messageline_handle, messageline_map);
45 DEF_CMD(messageline_clone)
47 struct pane *p = do_messageline_attach(ci->focus);
49 pane_clone_children(ci->home, p);
53 DEF_CMD(messageline_border)
55 struct mlinfo *mli = ci->home->data;
60 /* trigger a resize of children */
61 pane_damaged(ci->home, DAMAGED_SIZE);
62 return Efallthrough; /* Allow other panes to remove other borders */
65 DEF_CMD(messageline_msg)
67 struct mlinfo *mli = ci->home->data;
69 if (ci->str && (strcmp(ci->key, "Message:default") != 0 ||
70 mli->message == NULL)) {
72 call("Window:request:Keystroke-notify", ci->home);
73 call("Window:request:Mouse-event-notify", ci->home);
75 if (strcmp(ci->key, "Message:modal") == 0) {
78 mli->modal = strdup(ci->str);
84 mli->message = strdup(ci->str);
87 /* x==0 check ensures we only append message once when
88 * it comes in via a broadcast notification
90 if (ci->x == 0 && mli->log && ci->str[0])
91 call("doc:log:append", mli->log,
94 time(&mli->last_message);
95 pane_damaged(ci->home, DAMAGED_VIEW);
97 if (strcmp(ci->key, "Message:broadcast") == 0)
98 return 1; /* Acknowledge message */
100 return Efallthrough; /* allow other handlers */
103 DEF_CMD(messageline_abort)
105 struct mlinfo *mli = ci->home->data;
108 call("Window:request:Keystroke-notify", ci->home);
109 call("Window:request:Mouse-event-notify", ci->home);
112 mli->message = strdup("ABORTED");
115 time(&mli->last_message);
116 pane_damaged(ci->home, DAMAGED_VIEW);
120 DEF_CMD(messageline_refresh_size)
122 struct mlinfo *mli = ci->home->data;
123 struct pane *p = mli->line;
126 pane_resize(p, 0, ci->home->h,
127 ci->home->w, ci->home->h / 3);
129 pane_resize(mli->child, 0, 0,
130 ci->home->w, ci->home->h);
132 pane_resize(p, p->x, p->y, ci->home->w, ci->home->h/3);
133 call("render-line:measure", p, -1);
134 pane_resize(p, p->x, ci->home->h - p->h,
136 if (mli->child && ci->home->h > p->h)
137 pane_resize(mli->child, 0, 0,
141 pane_damaged(p, DAMAGED_REFRESH);
145 DEF_CMD(messageline_child_notify)
147 struct mlinfo *mli = ci->home->data;
152 if (ci->home->focus == ci->focus)
153 ci->home->focus = NULL;
157 pane_close(mli->child);
158 mli->child = ci->focus;
159 ci->home->focus = ci->focus;
164 DEF_CMD(messageline_notify)
166 /* Keystroke notification clears the message line */
167 struct mlinfo *mli = ci->home->data;
170 if (edlib_testing(ci->home))
177 mli->last_message = time(NULL);
178 pane_damaged(ci->home, DAMAGED_VIEW);
181 time(NULL) >= mli->last_message + wait_time) {
184 pane_damaged(ci->home, DAMAGED_VIEW);
186 if (!mli->message && !mli->modal) {
187 pane_drop_notifiers(ci->home, "Keystroke-notify");
188 pane_drop_notifiers(ci->home, "Mouse-event-notify");
193 static void pane_str(struct pane *p safe, char *s, char *attr)
195 struct mlinfo *mli = p->parent->data;
196 char *l = strconcat(p, SOH, attr, STX, s, ETX);
197 call("render-line:set", p, -1, NULL, l);
198 /* Allow message line to use up to 1/3 of total height */
199 pane_resize(p, p->x, p->y, p->w, p->parent->h/3);
200 call("render-line:measure", p, -1);
202 pane_resize(p, p->x, p->parent->h - p->h, p->w, p->h);
204 struct pane *c = mli->child;
205 pane_resize(c, 0, 0, c->w, p->parent->h - p->h);
210 DEF_CMD(messageline_refresh)
212 struct mlinfo *mli = ci->home->data;
214 if (mli->message && !mli->modal &&
215 time(NULL) >= mli->last_message + 30) {
218 pane_drop_notifiers(ci->home, "Keystroke-notify");
219 pane_drop_notifiers(ci->home, "Mouse-event-notify");
222 pane_str(mli->line, mli->modal, "bold,fg:magenta-60,bg:white");
223 else if (mli->message)
224 pane_str(mli->line, mli->message, "bold,fg:red,bg:cyan");
231 if (edlib_testing(ci->home))
235 strftime(buf, sizeof(buf), "%H:%M %d-%b-%Y", tm);
238 pane_str(mli->line, buf, "bold,fg:blue,rtab");
243 DEF_CMD(force_refresh)
245 pane_damaged(ci->home, DAMAGED_VIEW);
249 static struct pane *do_messageline_attach(struct pane *p safe)
252 struct pane *ret, *mlp;
254 ret = pane_register(p, 0, &messageline_handle.c);
258 call("editor:request:Message:broadcast", ret);
259 /* z=1 to avoid clone_children affecting it */
260 mlp = call_ret(pane, "attach-renderline", ret, 1);
265 /* Support wrapping */
266 attr_set_str(&mlp->attrs, "render:wrap", "yes");
267 pane_damaged(ret, DAMAGED_VIEW);
269 pane_take_focus(ret);
270 if (!edlib_testing(p))
271 /* This can introduce unwanted variablitiy in tests */
272 call_comm("event:timer", ret, &force_refresh, 15000);
274 mli->log = call_ret(pane, "docs:byname", p, 0, NULL, "*Messages*");
276 mli->log = call_ret(pane, "log:create", ret, 0, NULL,
282 DEF_CMD(messageline_attach)
286 ret = do_messageline_attach(ci->focus);
289 return comm_call(ci->comm2, "callback:attach", ret);
292 void edlib_init(struct pane *ed safe)
294 call_comm("global-set-command", ed, &messageline_attach, 0, NULL,
295 "attach-messageline");
299 messageline_map = key_alloc();
300 key_add(messageline_map, "Clone", &messageline_clone);
301 key_add(messageline_map, "Window:border", &messageline_border);
302 key_add(messageline_map, "Message", &messageline_msg);
303 key_add(messageline_map, "Message:modal", &messageline_msg);
304 key_add(messageline_map, "Message:default", &messageline_msg);
305 key_add(messageline_map, "Message:broadcast", &messageline_msg);
306 key_add(messageline_map, "Abort", &messageline_abort);
307 key_add(messageline_map, "Refresh:size", &messageline_refresh_size);
308 key_add(messageline_map, "Child-Notify",&messageline_child_notify);
309 key_add(messageline_map, "Keystroke-notify", &messageline_notify);
310 key_add(messageline_map, "Mouse-event-notify", &messageline_notify);
311 key_add(messageline_map, "Refresh:view", &messageline_refresh);