]> git.neil.brown.name Git - edlib.git/blob - doc-list.c
TODO: clean out done items.
[edlib.git] / doc-list.c
1 /*
2  * Copyright Neil Brown ©2022-2023 <neil@brown.name>
3  * May be distributed under terms of GPLv2 - see file:COPYING
4  *
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.
9  *
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.
12  *
13  */
14
15 #define PRIVATE_DOC_REF
16
17 struct doc_ref {
18         struct elmnt *p;
19         unsigned int i;
20 };
21
22 #define DOC_SHARESREF
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)
26 #include "core.h"
27 #include "misc.h"
28
29 struct elmnt {
30         struct list_head        list;
31         struct attrset          *attrs;
32 };
33
34 struct list {
35         struct doc              doc;
36         struct list_head        content;
37 };
38 #include "core-pane.h"
39
40 static inline wint_t list_next(struct pane *p safe, struct doc_ref *r safe, bool bytes)
41 {
42         struct list *l = p->doc_data;
43
44         if (r->p == NULL)
45                 return WEOF;
46
47         if (r->p == list_last_entry(&l->content, struct elmnt, list))
48                 r->p = NULL;
49         else
50                 r->p = list_next_entry(r->p, list);
51         return ' ';
52 }
53
54 static inline wint_t list_prev(struct pane *p safe, struct doc_ref *r safe, bool bytes)
55 {
56         struct list *l = p->doc_data;
57
58         if (r->p == list_first_entry_or_null(&l->content, struct elmnt, list))
59                 return WEOF;
60
61         if (r->p == NULL)
62                 r->p = list_last_entry(&l->content, struct elmnt, list);
63         else
64                 r->p = list_prev_entry(r->p, list);
65         return ' ';
66 }
67
68 DEF_CMD(list_char)
69 {
70         return do_char_byte(ci);
71 }
72
73 DEF_CMD(list_set_ref)
74 {
75         struct list *l = ci->home->doc_data;
76         struct mark *m = ci->mark;
77
78         if (!m)
79                 return Enoarg;
80         mark_to_end(ci->home, m, ci->num != 1);
81         if (list_empty(&l->content) || ci->num != 1)
82                 m->ref.p = NULL;
83         else
84                 m->ref.p = list_first_entry(&l->content, struct elmnt, list);
85         m->ref.i = 0;
86         return 1;
87 }
88
89 DEF_CMD(list_set_attr)
90 {
91         struct mark *m = ci->mark;
92         const char *attr = ci->str;
93         const char *val = ci->str2;
94         struct elmnt *e;
95
96         if (!m || !attr)
97                 return Enoarg;
98         e = m->ref.p;
99         if (!e)
100                 return Efallthrough;
101         attr_set_str(&e->attrs, attr, val);
102         pane_notify("doc:replaced-attr", ci->home, 1, m);
103         return 1;
104 }
105
106 DEF_CMD(list_get_attr)
107 {
108         struct mark *m = ci->mark;
109         const char *attr = ci->str;
110         const char *val = NULL;
111         struct elmnt *e;
112
113         if (!m || !attr)
114                 return Enoarg;
115         e = m->ref.p;
116         if (e)
117                 val = attr_find(e->attrs, attr);
118         if (!val)
119                 return Efallthrough;
120         comm_call(ci->comm2, "callback:get_attr", ci->focus, 0, m, val,
121                   0, NULL, attr);
122         return 1;
123 }
124
125 DEF_CMD(list_shares_ref)
126 {
127         return 1;
128 }
129
130 DEF_CMD(list_add_elmnt)
131 {
132         struct list *l = ci->home->doc_data;
133         struct mark *m = ci->mark;
134         struct elmnt *e;
135
136         if (!m)
137                 return Enoarg;
138         alloc(e, pane);
139         if (m->ref.p)
140                 list_add(&e->list, &m->ref.p->list);
141         else
142                 list_add_tail(&e->list, &l->content);
143         m->ref.p = e;
144         return 1;
145 }
146
147 DEF_CMD(list_del_elmnt)
148 {
149         struct list *l = ci->home->doc_data;
150         struct mark *m = ci->mark;
151         struct mark *m2;
152         struct elmnt *e;
153
154         if (!m)
155                 return Enoarg;
156         if (!m->ref.p)
157                 return Efalse;
158         e = m->ref.p;
159         while ((m2 = mark_next(m)) && m2->ref.p == e)
160                 m = m2;
161         if (e == list_last_entry(&l->content, struct elmnt, list))
162                 m->ref.p = NULL;
163         else
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;
167                 m = m2;
168         }
169         list_del(&e->list);
170         unalloc(e, pane);
171         return 1;
172 }
173
174 static char *key(struct list_head *le, const void *data)
175 {
176         const char *keyattr = data;
177         struct elmnt *e;
178         char *ret;
179         if (le == NULL || keyattr == NULL)
180                 return NULL;
181         e = container_of(le, struct elmnt, list);
182         ret = attr_find(e->attrs, keyattr);
183         return ret ?: "";
184 }
185
186 DEF_CMD(list_sort)
187 {
188         struct list *l = ci->home->doc_data;
189         struct mark *m;
190
191         if (!ci->str)
192                 return Enoarg;
193         /* First move all marks to end */
194         for (m = mark_first(&l->doc); m ; m = mark_next(m)) {
195                 m->ref.p = NULL;
196                 m->ref.i = 0;
197         }
198         sort_list(&l->content, key, ci->str);
199         return 1;
200 }
201
202 static struct map *list_map;
203 DEF_LOOKUP_CMD(list_handle, list_map);
204
205 DEF_CMD(list_new)
206 {
207         struct list *l;
208         struct pane *p;
209
210         p = doc_register(ci->home, &list_handle.c);
211         if (!p)
212                 return Efail;
213         l = p->doc_data;
214         INIT_LIST_HEAD(&l->content);
215
216         return comm_call(ci->comm2, "callback:doc", p);
217 }
218
219 DEF_CMD_CLOSED(list_close)
220 {
221         struct list *l = ci->home->doc_data;
222         struct elmnt *e;
223
224         while ((e = list_first_entry_or_null(&l->content,
225                                             struct elmnt, list)) != NULL) {
226                 attr_free(&e->attrs);
227                 list_del(&e->list);
228                 unalloc(e, pane);
229         }
230         return 1;
231 }
232
233 static void list_init_map(void)
234 {
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);
246 }
247
248 void edlib_init(struct pane *ed safe)
249 {
250         list_init_map();
251         call_comm("global-set-command", ed, &list_new, 0, NULL,
252                   "attach-doc-list");
253 }