1 #include "lib9.h"
2 #include "draw.h"
3 #include "tk.h"
4 #include "textw.h"
5
6 #define istring u.string
7 #define iwin u.win
8 #define imark u.mark
9 #define iline u.line
10
11 static char* tktmarkgravity(Tk*, char*, char**);
12 static char* tktmarknames(Tk*, char*, char**);
13 static char* tktmarknext(Tk*, char*, char**);
14 static char* tktmarkprevious(Tk*, char*, char**);
15 static char* tktmarkset(Tk*, char*, char**);
16 static char* tktmarkunset(Tk*, char*, char**);
17
18 TkCmdtab
19 tktmarkcmd[] =
20 {
21 "gravity", tktmarkgravity,
22 "names", tktmarknames,
23 "next", tktmarknext,
24 "previous", tktmarkprevious,
25 "set", tktmarkset,
26 "unset", tktmarkunset,
27 nil
28 };
29
30 char*
tktaddmarkinfo(TkText * tkt,char * name,TkTmarkinfo ** ret)31 tktaddmarkinfo(TkText *tkt, char *name, TkTmarkinfo **ret)
32 {
33 TkTmarkinfo *mi;
34
35 mi = malloc(sizeof(TkTmarkinfo));
36 if(mi == nil)
37 return TkNomem;
38
39 mi->name = strdup(name);
40 if(mi->name == nil) {
41 free(mi);
42 return TkNomem;
43 }
44 mi->gravity = Tkright;
45 mi->cur = nil;
46 mi->next = tkt->marks;
47 tkt->marks = mi;
48 *ret = mi;
49 return nil;
50 }
51
52 void
tktfreemarks(TkTmarkinfo * m)53 tktfreemarks(TkTmarkinfo *m)
54 {
55 TkTmarkinfo *n;
56
57 while(m != nil) {
58 n = m->next;
59 free(m->name);
60 free(m);
61 m = n;
62 }
63 }
64
65 TkTmarkinfo *
tktfindmark(TkTmarkinfo * m,char * name)66 tktfindmark(TkTmarkinfo *m, char *name)
67 {
68 while(m != nil) {
69 if(strcmp(m->name, name) == 0)
70 return m;
71 m = m->next;
72 }
73 return nil;
74 }
75
76 int
tktmarkind(Tk * tk,char * name,TkTindex * ans)77 tktmarkind(Tk *tk, char *name, TkTindex *ans)
78 {
79 TkTmarkinfo *mk;
80 TkText *tkt = TKobj(TkText, tk);
81
82 if(strcmp(name, "current") == 0) {
83 tktxyind(tk, tkt->current.x, tkt->current.y, ans);
84 return 1;
85 }
86
87 mk = tktfindmark(tkt->marks, name);
88 if(mk == nil || mk->cur == nil)
89 return 0;
90
91 ans->item = mk->cur;
92 ans->line = tktitemline(ans->item);
93 ans->pos = 0;
94 return 1;
95 }
96
97 char*
tktmarkparse(Tk * tk,char ** parg,TkTmarkinfo ** ret)98 tktmarkparse(Tk *tk, char **parg, TkTmarkinfo **ret)
99 {
100 char *e, *buf;
101 TkText *tkt = TKobj(TkText, tk);
102
103 buf = mallocz(Tkmaxitem, 0);
104 if(buf == nil)
105 return TkNomem;
106
107 *parg = tkword(tk->env->top, *parg, buf, buf+Tkmaxitem, nil);
108 if(*buf == '\0') {
109 free(buf);
110 return TkOparg;
111 }
112
113 *ret = tktfindmark(tkt->marks, buf);
114 if(*ret == nil) {
115 e = tktaddmarkinfo(tkt, buf, ret);
116 if(e != nil) {
117 free(buf);
118 return e;
119 }
120 }
121 free(buf);
122
123 return nil;
124 }
125
126 /*
127 * Insert mark before ixnew, first removing it from old place, if any.
128 * Make sure ixnew continues to point after mark.
129 */
130 char*
tktmarkmove(Tk * tk,TkTmarkinfo * m,TkTindex * ixnew)131 tktmarkmove(Tk *tk, TkTmarkinfo *m, TkTindex *ixnew)
132 {
133 char *e;
134 int deleted, split;
135 TkTitem *i;
136 TkTindex ix, pix;
137 TkText *tkt = TKobj(TkText, tk);
138
139 deleted = 0;
140 if(m->cur != nil) {
141 if(m->cur == ixnew->item)
142 return nil;
143 ix.item = m->cur;
144 ix.line = tktitemline(m->cur);
145 ix.pos = 0;
146 tktremitem(tkt, &ix);
147 deleted = 1;
148 }
149
150 /* XXX - Tad: memory leak on 'i' if something fails later? */
151 e = tktnewitem(TkTmark, 0, &i);
152 if(e != nil)
153 return e;
154
155 i->imark = m;
156 m->cur = i;
157
158 /* keep adjacent marks sorted: all rights, then all lefts */
159 if(m->gravity == Tkright) {
160 while(ixnew->item->kind == TkTmark && ixnew->item->imark->gravity == Tkleft)
161 if(!tktadjustind(tkt, TkTbyitem, ixnew))
162 break;
163 }
164 else {
165 for(;;) {
166 pix = *ixnew;
167 if(!tktadjustind(tkt, TkTbyitemback, &pix))
168 break;
169 if(pix.item->kind == TkTmark && pix.item->imark->gravity == Tkright)
170 *ixnew = pix;
171 else
172 break;
173 }
174 }
175
176 split = (ixnew->pos > 0);
177 e = tktsplititem(ixnew);
178 if(e != nil)
179 return e;
180
181 e = tktiteminsert(tkt, ixnew, i);
182 if(e != nil)
183 return nil;
184
185 if(strcmp(m->name, "insert") == 0 || split) {
186 if(deleted && ix.line != ixnew->line) {
187 tktfixgeom(tk, tktprevwrapline(tk, ix.line), ix.line, 0);
188 /*
189 * this is ok only because tktfixgeom cannot
190 * free mark items, and we know that i is a mark item.
191 */
192 ixnew->item = i;
193 ixnew->line = tktitemline(i);
194 ixnew->pos = 0;
195 }
196 tktfixgeom(tk, tktprevwrapline(tk, ixnew->line), ixnew->line, 0);
197 tktextsize(tk, 1);
198 }
199
200 ixnew->item = i;
201 ixnew->line = tktitemline(i);
202 ixnew->pos = 0;
203 return nil;
204 }
205
206 /* Text Mark Commands (+ means implemented)
207 +gravity
208 +names
209 +next
210 +previous
211 +set
212 +unset
213 */
214
215 static char*
tktmarkgravity(Tk * tk,char * arg,char ** val)216 tktmarkgravity(Tk *tk, char *arg, char **val)
217 {
218 char *e;
219 TkTmarkinfo *m;
220 char *buf;
221
222 e = tktmarkparse(tk, &arg, &m);
223 if(e != nil)
224 return e;
225
226 if(*arg == '\0')
227 return tkvalue(val, (m->gravity & Tkleft)? "left" : "right");
228 else {
229 buf = mallocz(Tkmaxitem, 0);
230 if(buf == nil)
231 return TkNomem;
232 tkword(tk->env->top, arg, buf, buf+Tkmaxitem, nil);
233 if(strcmp(buf, "left") == 0)
234 m->gravity = Tkleft;
235 else
236 if(strcmp(buf, "right") == 0)
237 m->gravity = Tkright;
238 else {
239 free(buf);
240 return TkBadcm;
241 }
242 free(buf);
243 }
244 return nil;
245 }
246
247 static char*
tktmarknames(Tk * tk,char * arg,char ** val)248 tktmarknames(Tk *tk, char *arg, char **val)
249 {
250 char *r, *fmt;
251 TkTmarkinfo *m;
252 TkText *tkt = TKobj(TkText, tk);
253
254 USED(arg);
255
256 fmt = "%s";
257 for(m = tkt->marks; m != nil; m = m->next) {
258 r = tkvalue(val, fmt, m->name);
259 if(r != nil)
260 return r;
261 fmt = " %s";
262 }
263 return nil;
264 }
265
266 static char*
tktmarknext(Tk * tk,char * arg,char ** val)267 tktmarknext(Tk *tk, char *arg, char **val)
268 {
269 char *e;
270 TkTmarkinfo *mix;
271 TkTindex ix, ixend;
272 TkText *tkt = TKobj(TkText, tk);
273
274 /* special behavior if specified index is a mark name */
275 mix = tktfindmark(tkt->marks, arg);
276
277 e = tktindparse(tk, &arg, &ix);
278 if(e != nil)
279 return e;
280
281 if(mix != nil)
282 tktadjustind(tkt, TkTbyitem, &ix);
283
284 /* special behavior if index is 'end' */
285 tktendind(tkt, &ixend);
286 if(tktindcompare(tkt, &ix, TkEq, &ixend)) {
287 do {
288 tktadjustind(tkt, TkTbyitemback, &ix);
289 } while(ix.item->kind == TkTmark);
290 }
291
292 do {
293 if(ix.item->kind == TkTmark)
294 return tkvalue(val, "%s", ix.item->imark->name);
295
296 } while(tktadjustind(tkt, TkTbyitem, &ix));
297
298 return nil;
299 }
300
301 static char*
tktmarkprevious(Tk * tk,char * arg,char ** val)302 tktmarkprevious(Tk *tk, char *arg, char **val)
303 {
304 char *e;
305 TkTindex ix;
306 TkText *tkt = TKobj(TkText, tk);
307
308 e = tktindparse(tk, &arg, &ix);
309 if(e != nil)
310 return e;
311
312 while(tktadjustind(tkt, TkTbyitemback, &ix)) {
313 if(ix.item->kind == TkTmark)
314 return tkvalue(val, "%s", ix.item->imark->name);
315 }
316
317 return nil;
318 }
319
320 /* XXX - Tad: possible memory leak here */
321 static char*
tktmarkset(Tk * tk,char * arg,char ** val)322 tktmarkset(Tk *tk, char *arg, char **val)
323 {
324 char *e;
325 TkTmarkinfo *m;
326 TkTindex ixnew;
327
328 USED(val);
329
330 e = tktmarkparse(tk, &arg, &m);
331 if(e != nil)
332 return e;
333 e = tktindparse(tk, &arg, &ixnew);
334 if(e != nil)
335 return e;
336
337 return tktmarkmove(tk, m, &ixnew);
338 }
339
340 static char*
tktmarkunset(Tk * tk,char * arg,char ** val)341 tktmarkunset(Tk *tk, char *arg, char **val)
342 {
343 TkText *tkt;
344 TkTmarkinfo *m, **p;
345 TkTindex ix;
346 char *e;
347 int resize;
348
349 USED(val);
350
351 tkt = TKobj(TkText, tk);
352
353 e = tktmarkparse(tk, &arg, &m);
354 if(e != nil)
355 return e;
356
357 resize = 0;
358 while(m != nil) {
359 if(strcmp(m->name, "insert") == 0 || strcmp(m->name, "current") == 0)
360 return TkBadvl;
361
362 if(m->cur != nil) {
363 ix.item = m->cur;
364 ix.line = tktitemline(m->cur);
365 ix.pos = 0;
366 tktremitem(tkt, &ix);
367 tktfixgeom(tk, tktprevwrapline(tk, ix.line), ix.line, 0);
368 resize = 1;
369 }
370
371 for(p = &tkt->marks; *p != nil; p = &(*p)->next) {
372 if(*p == m) {
373 *p = m->next;
374 break;
375 }
376 }
377 m->next = nil;
378 tktfreemarks(m);
379
380 if(*arg != '\0') {
381 e = tktmarkparse(tk, &arg, &m);
382 if(e != nil)
383 return e;
384 }
385 else
386 m = nil;
387 }
388 if (resize)
389 tktextsize(tk, 1);
390 return nil;
391 }
392
393