]> git.neil.brown.name Git - edlib.git/blob - lib-crop.c
TODO: clean out done items.
[edlib.git] / lib-crop.c
1 /*
2  * Copyright Neil Brown ©2016-2023 <neil@brown.name>
3  * May be distributed under terms of GPLv2 - see file:COPYING
4  *
5  * lib-crop: limit access to a range within a document.
6  * Given two marks which refer to the parent document, we
7  * pass on any commands without marks, or with marks in the
8  * given range.  If either mark is moved beyond the range, we move
9  * it back to the boundary and fail the request.
10  */
11
12 #include <stdlib.h>
13 #include <string.h>
14
15 #define PANE_DATA_TYPE struct crop_data
16 #define DOC_NEXT crop_next
17 #define DOC_PREV crop_prev
18 #include "core.h"
19
20 struct crop_data {
21         struct mark *start safe;
22         struct mark *end safe;
23 };
24 #include "core-pane.h"
25
26 static bool in_range(struct mark *m, struct crop_data *cd safe)
27 {
28         if (!m)
29                 /* NULL is always in range */
30                 return True;
31         if (m->seq >= cd->start->seq && m->seq <= cd->end->seq)
32                 return True;
33         /* I think I want strict ordering at the point, so don't test mark_same */
34         return False;
35 }
36
37 static bool crop(struct mark *m, struct crop_data *cd safe)
38 {
39         /* If mark is outside of range, move it back, and report if more was
40          * required than just updating the ->seq
41          */
42         if (!m || in_range(m, cd))
43                 return False;
44
45         if (m->seq < cd->start->seq) {
46                 if (mark_same(m, cd->start)) {
47                         mark_to_mark(m, cd->start);
48                         return False;
49                 }
50                 mark_to_mark(m, cd->start);
51         }
52         if (m->seq > cd->end->seq) {
53                 if (mark_same(m, cd->end)) {
54                         mark_to_mark(m, cd->end);
55                         return False;
56                 }
57                 mark_to_mark(m, cd->end);
58         }
59         return True;
60 }
61
62 DEF_CMD_CLOSED(crop_close)
63 {
64         struct crop_data *cd = ci->home->data;
65
66         mark_free(cd->start);
67         mark_free(cd->end);
68         return 1;
69 }
70
71 DEF_CMD(crop_write)
72 {
73         struct pane *p = ci->home->parent;
74         struct crop_data *cd = ci->home->data;
75
76         return home_call(p, ci->key, ci->focus, ci->num,
77                          ci->mark ?: cd->start,
78                          ci->str, ci->num2,
79                          ci->mark2 ?: cd->end, ci->str2,
80                          0,0, ci->comm2);
81 }
82
83 static inline wint_t crop_next(struct pane *home safe, struct mark *mark safe,
84                                struct doc_ref *r, bool bytes)
85 {
86         struct pane *p = home->parent;
87         struct crop_data *cd = home->data;
88         int move = r == &mark->ref;
89         int ret;
90
91         /* Always force marks to be in range */
92         crop(mark, cd);
93
94         ret = home_call(p, bytes ? "doc:byte" : "doc:char", home,
95                         move ? 1 : 0,
96                         mark, NULL,
97                         move ? 0 : 1);
98         if (crop(mark, cd))
99                 ret = WEOF;
100
101         if (!move && mark_same(mark, cd->end))
102                 ret = WEOF;
103         return ret;
104 }
105
106 static inline wint_t crop_prev(struct pane *home safe, struct mark *mark safe,
107                                struct doc_ref *r, bool bytes)
108 {
109         struct pane *p = home->parent;
110         struct crop_data *cd = home->data;
111         int move = r == &mark->ref;
112         int ret;
113
114         /* Always force marks to be in range */
115         crop(mark, cd);
116
117         ret = home_call(p, bytes ? "doc:byte" : "doc:char", home,
118                         move ? -1 : 0,
119                         mark, NULL,
120                         move ? 0 : -1);
121         if (crop(mark, cd))
122                 ret = WEOF;
123
124         if (!move && mark_same(mark, cd->start))
125                 ret = WEOF;
126
127         return ret;
128 }
129
130 DEF_CMD(crop_char)
131 {
132         return do_char_byte(ci);
133 }
134
135 DEF_CMD(crop_clip)
136 {
137         struct crop_data *cd = ci->home->data;
138
139         mark_clip(cd->start, ci->mark, ci->mark2, !!ci->num);
140         mark_clip(cd->end, ci->mark, ci->mark2, !!ci->num);
141         return Efallthrough;
142 }
143
144 DEF_CMD(crop_content)
145 {
146         struct crop_data *cd = ci->home->data;
147         struct mark *m, *m2;
148         int ret;
149
150         if (!ci->mark)
151                 return Enoarg;
152         m = mark_dup(ci->mark);
153         crop(m, cd);
154         crop(ci->mark2, cd);
155         if (ci->mark2)
156                 m2 = ci->mark2;
157         else
158                 m2 = mark_dup(cd->end);
159         ret = home_call_comm(ci->home->parent, ci->key, ci->home,
160                              ci->comm2, 0, m, NULL, 0, m2);
161         mark_free(m);
162         if (m2 != ci->mark2)
163                 mark_free(m2);
164         return ret;
165 }
166
167 DEF_CMD(crop_generic)
168 {
169         struct pane *p = ci->home->parent;
170         struct crop_data *cd = ci->home->data;
171         int ret;
172
173         if (!ci->mark && !ci->mark2)
174                 /* No mark, do give it straight to parent */
175                 return home_call(p, ci->key, ci->focus, ci->num,
176                                  NULL, ci->str, ci->num2, NULL, ci->str2,
177                                  0,0, ci->comm2);
178
179         /* Always force marks to be in range */
180         crop(ci->mark, cd);
181         crop(ci->mark2, cd);
182
183         ret = home_call(p, ci->key, ci->focus, ci->num,
184                         ci->mark, ci->str, ci->num2, ci->mark2, ci->str2, 0,0, ci->comm2);
185         if (crop(ci->mark, cd) || crop(ci->mark2, cd)) {
186                 if (strcmp(ci->key, "doc:set-ref") != 0)
187                         ret = Einval;
188         }
189         return ret;
190 }
191
192 static struct map *crop_map safe;
193 DEF_LOOKUP_CMD(crop_handle, crop_map);
194
195 DEF_CMD(crop_attach)
196 {
197         struct pane *p;
198         struct crop_data *cd;
199
200         if (!ci->mark || !ci->mark2)
201                 return Enoarg;
202         if (ci->mark->seq >= ci->mark2->seq)
203                 return Einval;
204         p = pane_register(ci->focus, 0, &crop_handle.c);
205         if (!p)
206                 return Efail;
207
208         cd = p->data;
209         cd->start = mark_dup(ci->mark);
210         cd->end = mark_dup(ci->mark2);
211
212         return comm_call(ci->comm2, "callback:attach", p);
213 }
214
215 void edlib_init(struct pane *ed safe)
216 {
217         call_comm("global-set-command", ed, &crop_attach, 0, NULL, "attach-crop");
218         if ((void*)crop_map)
219                 return;
220         crop_map = key_alloc();
221         key_add_prefix(crop_map, "doc:", &crop_generic);
222         key_add(crop_map, "Close", &crop_close);
223         key_add(crop_map, "doc:write_file", &crop_write);
224         key_add(crop_map, "doc:char", &crop_char);
225         key_add(crop_map, "doc:byte", &crop_char);
226         key_add(crop_map, "doc:content", &crop_content);
227         key_add(crop_map, "doc:content-bytes", &crop_content);
228         key_add(crop_map, "Notify:clip", &crop_clip);
229 }