tmark.c 6.9 KB

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