2 * Copyright Neil Brown ©2022-2023 <neil@brown.name>
3 * May be distributed under terms of GPLv2 - see file:COPYING
5 * doc-list: present an arbitrary list of items as a document.
6 * This was initially created to support menus. A doc-list is somewhat
7 * similar to doc-dir or doc-docs in each each element in the document
8 * has the primary content in the attributes associated with the element.
10 * Elements can be added after a mark with doc:list-add. The mark remains
11 * before the element so doc:set-attr can add appropriate attributes.
15 #define PRIVATE_DOC_REF
23 #define DOC_DATA_TYPE struct list
24 #define DOC_NEXT(d,m,r,b) list_next(d,r,b)
25 #define DOC_PREV(d,m,r,b) list_prev(d,r,b)
30 struct list_head list;
31 struct attrset *attrs;
36 struct list_head content;
38 #include "core-pane.h"
40 static inline wint_t list_next(struct pane *p safe, struct doc_ref *r safe, bool bytes)
42 struct list *l = p->doc_data;
47 if (r->p == list_last_entry(&l->content, struct elmnt, list))
50 r->p = list_next_entry(r->p, list);
54 static inline wint_t list_prev(struct pane *p safe, struct doc_ref *r safe, bool bytes)
56 struct list *l = p->doc_data;
58 if (r->p == list_first_entry_or_null(&l->content, struct elmnt, list))
62 r->p = list_last_entry(&l->content, struct elmnt, list);
64 r->p = list_prev_entry(r->p, list);
70 return do_char_byte(ci);
75 struct list *l = ci->home->doc_data;
76 struct mark *m = ci->mark;
80 mark_to_end(ci->home, m, ci->num != 1);
81 if (list_empty(&l->content) || ci->num != 1)
84 m->ref.p = list_first_entry(&l->content, struct elmnt, list);
89 DEF_CMD(list_set_attr)
91 struct mark *m = ci->mark;
92 const char *attr = ci->str;
93 const char *val = ci->str2;
101 attr_set_str(&e->attrs, attr, val);
102 pane_notify("doc:replaced-attr", ci->home, 1, m);
106 DEF_CMD(list_get_attr)
108 struct mark *m = ci->mark;
109 const char *attr = ci->str;
110 const char *val = NULL;
117 val = attr_find(e->attrs, attr);
120 comm_call(ci->comm2, "callback:get_attr", ci->focus, 0, m, val,
125 DEF_CMD(list_shares_ref)
130 DEF_CMD(list_add_elmnt)
132 struct list *l = ci->home->doc_data;
133 struct mark *m = ci->mark;
140 list_add(&e->list, &m->ref.p->list);
142 list_add_tail(&e->list, &l->content);
147 DEF_CMD(list_del_elmnt)
149 struct list *l = ci->home->doc_data;
150 struct mark *m = ci->mark;
159 while ((m2 = mark_next(m)) && m2->ref.p == e)
161 if (e == list_last_entry(&l->content, struct elmnt, list))
164 m->ref.p = list_next_entry(e, list);
165 while ((m2 = mark_prev(m)) && m2->ref.p == e) {
166 m2->ref.p = m->ref.p;
174 static char *key(struct list_head *le, const void *data)
176 const char *keyattr = data;
179 if (le == NULL || keyattr == NULL)
181 e = container_of(le, struct elmnt, list);
182 ret = attr_find(e->attrs, keyattr);
188 struct list *l = ci->home->doc_data;
193 /* First move all marks to end */
194 for (m = mark_first(&l->doc); m ; m = mark_next(m)) {
198 sort_list(&l->content, key, ci->str);
202 static struct map *list_map;
203 DEF_LOOKUP_CMD(list_handle, list_map);
210 p = doc_register(ci->home, &list_handle.c);
214 INIT_LIST_HEAD(&l->content);
216 return comm_call(ci->comm2, "callback:doc", p);
219 DEF_CMD_CLOSED(list_close)
221 struct list *l = ci->home->doc_data;
224 while ((e = list_first_entry_or_null(&l->content,
225 struct elmnt, list)) != NULL) {
226 attr_free(&e->attrs);
233 static void list_init_map(void)
235 list_map = key_alloc();
236 key_add_chain(list_map, doc_default_cmd);
237 key_add(list_map, "doc:char", &list_char);
238 key_add(list_map, "doc:set-ref", &list_set_ref);
239 key_add(list_map, "doc:set-attr", &list_set_attr);
240 key_add(list_map, "doc:get-attr", &list_get_attr);
241 key_add(list_map, "doc:shares-ref", &list_shares_ref);
242 key_add(list_map, "doc:list-add", &list_add_elmnt);
243 key_add(list_map, "doc:list-del", &list_del_elmnt);
244 key_add(list_map, "doc:list-sort", &list_sort);
245 key_add(list_map, "Close", &list_close);
248 void edlib_init(struct pane *ed safe)
251 call_comm("global-set-command", ed, &list_new, 0, NULL,