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* 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 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 * 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 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* 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* 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* 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* 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* 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* 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* 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* 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