text.c 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431
  1. #include <u.h>
  2. #include <libc.h>
  3. #include <draw.h>
  4. #include <thread.h>
  5. #include <mouse.h>
  6. #include <keyboard.h>
  7. #include <control.h>
  8. typedef struct Text Text;
  9. struct Text
  10. {
  11. Control;
  12. int border;
  13. int topline;
  14. int scroll;
  15. int nvis;
  16. int lastbut;
  17. CFont *font;
  18. CImage *image;
  19. CImage *textcolor;
  20. CImage *bordercolor;
  21. CImage *selectcolor;
  22. Rune **line;
  23. int selectmode;
  24. uchar *selected;
  25. int nline;
  26. int align;
  27. };
  28. enum
  29. {
  30. Selsingle,
  31. Selmulti,
  32. };
  33. enum{
  34. EAccumulate,
  35. EAdd,
  36. EAlign,
  37. EBorder,
  38. EBordercolor,
  39. EClear,
  40. EDelete,
  41. EFocus,
  42. EFont,
  43. EHide,
  44. EImage,
  45. ERect,
  46. EReplace,
  47. EReveal,
  48. EScroll,
  49. ESelect,
  50. ESelectcolor,
  51. ESelectmode,
  52. EShow,
  53. ESize,
  54. ETextcolor,
  55. ETopline,
  56. EValue,
  57. };
  58. static char *cmds[] = {
  59. [EAccumulate] = "accumulate",
  60. [EAdd] = "add",
  61. [EAlign] = "align",
  62. [EBorder] = "border",
  63. [EBordercolor] = "bordercolor",
  64. [EClear] = "clear",
  65. [EDelete] = "delete",
  66. [EFocus] = "focus",
  67. [EFont] = "font",
  68. [EHide] = "hide",
  69. [EImage] = "image",
  70. [ERect] = "rect",
  71. [EReplace] = "replace",
  72. [EReveal] = "reveal",
  73. [EScroll] = "scroll",
  74. [ESelect] = "select",
  75. [ESelectcolor] = "selectcolor",
  76. [ESelectmode] = "selectmode",
  77. [EShow] = "show",
  78. [ESize] = "size",
  79. [ETextcolor] = "textcolor",
  80. [ETopline] = "topline",
  81. [EValue] = "value",
  82. nil
  83. };
  84. static void textshow(Text*);
  85. static void texttogglei(Text*, int);
  86. static int texttoggle(Text*, Point);
  87. static void
  88. textmouse(Control *c, Mouse *m)
  89. {
  90. Text *t;
  91. int sel;
  92. t = (Text*)c;
  93. if(t->lastbut != (m->buttons&7)){
  94. if(m->buttons & 7){
  95. sel = texttoggle(t, m->xy);
  96. if(sel >= 0)
  97. chanprint(t->event, "%q: select %d %d",
  98. t->name, sel, t->selected[sel] ? (m->buttons & 7) : 0);
  99. }
  100. t->lastbut = m->buttons & 7;
  101. }
  102. }
  103. static void
  104. textfree(Control *c)
  105. {
  106. int i;
  107. Text *t;
  108. t = (Text*)c;
  109. _putctlfont(t->font);
  110. _putctlimage(t->image);
  111. _putctlimage(t->textcolor);
  112. _putctlimage(t->bordercolor);
  113. _putctlimage(t->selectcolor);
  114. for(i=0; i<t->nline; i++)
  115. free(t->line[i]);
  116. free(t->line);
  117. free(t->selected);
  118. }
  119. static void
  120. textshow(Text *t)
  121. {
  122. Rectangle r, tr;
  123. Point p;
  124. int i, ntext;
  125. Font *f;
  126. Rune *text;
  127. if (t->hidden)
  128. return;
  129. r = t->rect;
  130. f = t->font->font;
  131. draw(t->screen, r, t->image->image, nil, t->image->image->r.min);
  132. if(t->border > 0){
  133. border(t->screen, r, t->border, t->bordercolor->image, t->bordercolor->image->r.min);
  134. r = insetrect(r, t->border);
  135. }
  136. tr = r;
  137. t->nvis = Dy(r)/f->height;
  138. for(i=t->topline; i<t->nline && i<t->topline+t->nvis; i++){
  139. text = t->line[i];
  140. ntext = runestrlen(text);
  141. r.max.y = r.min.y+f->height;
  142. if(t->selected[i])
  143. draw(t->screen, r, t->selectcolor->image, nil, ZP);
  144. p = _ctlalignpoint(r,
  145. runestringnwidth(f, text, ntext),
  146. f->height, t->align);
  147. _string(t->screen, p, t->textcolor->image,
  148. ZP, f, nil, text, ntext, tr,
  149. nil, ZP, SoverD);
  150. r.min.y += f->height;
  151. }
  152. flushimage(display, 1);
  153. }
  154. static void
  155. textctl(Control *c, CParse *cp)
  156. {
  157. int cmd, i, n;
  158. Rectangle r;
  159. Text *t;
  160. Rune *rp;
  161. t = (Text*)c;
  162. cmd = _ctllookup(cp->args[0], cmds, nelem(cmds));
  163. switch(cmd){
  164. default:
  165. ctlerror("%q: unrecognized message '%s'", t->name, cp->str);
  166. break;
  167. case EAlign:
  168. _ctlargcount(t, cp, 2);
  169. t->align = _ctlalignment(cp->args[1]);
  170. break;
  171. case EBorder:
  172. _ctlargcount(t, cp, 2);
  173. if(cp->iargs[1] < 0)
  174. ctlerror("%q: bad border: %c", t->name, cp->str);
  175. t->border = cp->iargs[1];
  176. break;
  177. case EBordercolor:
  178. _ctlargcount(t, cp, 2);
  179. _setctlimage(t, &t->bordercolor, cp->args[1]);
  180. break;
  181. case EClear:
  182. _ctlargcount(t, cp, 1);
  183. for(i=0; i<t->nline; i++)
  184. free(t->line[i]);
  185. free(t->line);
  186. free(t->selected);
  187. t->line = ctlmalloc(sizeof(Rune*));
  188. t->selected = ctlmalloc(1);
  189. t->nline = 0;
  190. textshow(t);
  191. break;
  192. case EDelete:
  193. _ctlargcount(t, cp, 2);
  194. i = cp->iargs[1];
  195. if(i<0 || i>=t->nline)
  196. ctlerror("%q: line number out of range: %s", t->name, cp->str);
  197. free(t->line[i]);
  198. memmove(t->line+i, t->line+i+1, (t->nline-(i+1))*sizeof(Rune*));
  199. memmove(t->selected+i, t->selected+i+1, t->nline-(i+1));
  200. t->nline--;
  201. textshow(t);
  202. break;
  203. case EFocus:
  204. break;
  205. case EFont:
  206. _ctlargcount(t, cp, 2);
  207. _setctlfont(t, &t->font, cp->args[1]);
  208. break;
  209. case EHide:
  210. _ctlargcount(t, cp, 1);
  211. t->hidden = 1;
  212. break;
  213. case EImage:
  214. _ctlargcount(t, cp, 2);
  215. _setctlimage(t, &t->image, cp->args[1]);
  216. break;
  217. case ERect:
  218. _ctlargcount(t, cp, 5);
  219. r.min.x = cp->iargs[1];
  220. r.min.y = cp->iargs[2];
  221. r.max.x = cp->iargs[3];
  222. r.max.y = cp->iargs[4];
  223. if(Dx(r)<=0 || Dy(r)<=0)
  224. ctlerror("%q: bad rectangle: %s", t->name, cp->str);
  225. t->rect = r;
  226. t->nvis = (Dy(r)-2*t->border)/t->font->font->height;
  227. break;
  228. case EReplace:
  229. _ctlargcount(t, cp, 3);
  230. i = cp->iargs[1];
  231. if(i<0 || i>=t->nline)
  232. ctlerror("%q: line number out of range: %s", t->name, cp->str);
  233. free(t->line[i]);
  234. t->line[i] = _ctlrunestr(cp->args[2]);
  235. textshow(t);
  236. break;
  237. case EReveal:
  238. _ctlargcount(t, cp, 1);
  239. t->hidden = 0;
  240. textshow(t);
  241. break;
  242. case EScroll:
  243. _ctlargcount(t, cp, 2);
  244. t->scroll = cp->iargs[1];
  245. break;
  246. case ESelect:
  247. if(cp->nargs!=2 && cp->nargs!=3)
  248. badselect:
  249. ctlerror("%q: bad select message: %s", t->name, cp->str);
  250. if(cp->nargs == 2){
  251. if(strcmp(cp->args[1], "all") == 0){
  252. memset(t->selected, 1, t->nline);
  253. break;
  254. }
  255. if(strcmp(cp->args[1], "none") == 0){
  256. memset(t->selected, 0, t->nline);
  257. break;
  258. }
  259. if(cp->args[1][0]<'0' && '9'<cp->args[1][0])
  260. goto badselect;
  261. texttogglei(t, cp->iargs[1]);
  262. break;
  263. }
  264. if(cp->iargs[1]<0 || cp->iargs[1]>=t->nline)
  265. ctlerror("%q: selection index out of range (nline %d): %s", t->name, t->nline, cp->str);
  266. if(t->selected[cp->iargs[1]] != (cp->iargs[2]!=0))
  267. texttogglei(t, cp->iargs[1]);
  268. break;
  269. case ESelectcolor:
  270. _ctlargcount(t, cp, 2);
  271. _setctlimage(t, &t->selectcolor, cp->args[1]);
  272. break;
  273. case ESelectmode:
  274. _ctlargcount(t, cp, 2);
  275. if(strcmp(cp->args[1], "single") == 0)
  276. t->selectmode = Selsingle;
  277. else if(strncmp(cp->args[1], "multi", 5) == 0)
  278. t->selectmode = Selmulti;
  279. break;
  280. case EShow:
  281. _ctlargcount(t, cp, 1);
  282. textshow(t);
  283. break;
  284. case ESize:
  285. if (cp->nargs == 3)
  286. r.max = Pt(10000, 10000);
  287. else{
  288. _ctlargcount(t, cp, 5);
  289. r.max.x = cp->iargs[3];
  290. r.max.y = cp->iargs[4];
  291. }
  292. r.min.x = cp->iargs[1];
  293. r.min.y = cp->iargs[2];
  294. if(r.min.x<=0 || r.min.y<=0 || r.max.x<=0 || r.max.y<=0 || r.max.x < r.min.x || r.max.y < r.min.y)
  295. ctlerror("%q: bad sizes: %s", t->name, cp->str);
  296. t->size.min = r.min;
  297. t->size.max = r.max;
  298. break;
  299. case ETextcolor:
  300. _ctlargcount(t, cp, 2);
  301. _setctlimage(t, &t->textcolor, cp->args[1]);
  302. break;
  303. case ETopline:
  304. _ctlargcount(t, cp, 2);
  305. i = cp->iargs[1];
  306. if(i < 0)
  307. i = 0;
  308. if(i > t->nline)
  309. i = t->nline;
  310. if(t->topline != i){
  311. t->topline = i;
  312. textshow(t);
  313. }
  314. break;
  315. case EValue:
  316. /* set contents to single line */
  317. /* free existing text and fall through to add */
  318. for(i=0; i<t->nline; i++){
  319. free(t->line[i]);
  320. t->line[i] = nil;
  321. }
  322. t->nline = 0;
  323. t->topline = 0;
  324. /* fall through */
  325. case EAccumulate:
  326. case EAdd:
  327. switch (cp->nargs) {
  328. default:
  329. ctlerror("%q: wrong argument count in '%s'", t->name, cp->str);
  330. case 2:
  331. n = t->nline;
  332. break;
  333. case 3:
  334. n = cp->iargs[1];
  335. if(n<0 || n>t->nline)
  336. ctlerror("%q: line number out of range: %s", t->name, cp->str);
  337. break;
  338. }
  339. rp = _ctlrunestr(cp->args[cp->nargs-1]);
  340. t->line = ctlrealloc(t->line, (t->nline+1)*sizeof(Rune*));
  341. memmove(t->line+n+1, t->line+n, (t->nline-n)*sizeof(Rune*));
  342. t->line[n] = rp;
  343. t->selected = ctlrealloc(t->selected, t->nline+1);
  344. memmove(t->selected+n+1, t->selected+n, t->nline-n);
  345. t->selected[n] = (t->selectmode==Selmulti && cmd!=EAccumulate);
  346. t->nline++;
  347. if(t->scroll) {
  348. if(n > t->topline + (t->nvis - 1)){
  349. t->topline = n - (t->nvis - 1);
  350. if(t->topline < 0)
  351. t->topline = 0;
  352. }
  353. if(n < t->topline)
  354. t->topline = n;
  355. }
  356. if(cmd != EAccumulate)
  357. if(t->scroll || t->nline<=t->topline+t->nvis)
  358. textshow(t);
  359. break;
  360. }
  361. }
  362. static void
  363. texttogglei(Text *t, int i)
  364. {
  365. int prev;
  366. if(t->selectmode == Selsingle){
  367. /* clear the others */
  368. prev = t->selected[i];
  369. memset(t->selected, 0, t->nline);
  370. t->selected[i] = prev;
  371. }
  372. t->selected[i] ^= 1;
  373. textshow(t);
  374. }
  375. static int
  376. texttoggle(Text *t, Point p)
  377. {
  378. Rectangle r;
  379. int i;
  380. r = t->rect;
  381. if(t->border > 0)
  382. r = insetrect(r, t->border);
  383. if(!ptinrect(p, r))
  384. return -1;
  385. i = (p.y-r.min.y)/t->font->font->height;
  386. i += t->topline;
  387. if(i >= t->nline)
  388. return -1;
  389. texttogglei(t, i);
  390. return i;
  391. }
  392. Control*
  393. createtext(Controlset *cs, char *name)
  394. {
  395. Text *t;
  396. t = (Text*)_createctl(cs, "text", sizeof(Text), name);
  397. t->line = ctlmalloc(sizeof(Rune*));
  398. t->selected = ctlmalloc(1);
  399. t->nline = 0;
  400. t->image = _getctlimage("white");
  401. t->textcolor = _getctlimage("black");
  402. t->bordercolor = _getctlimage("black");
  403. t->selectcolor = _getctlimage("yellow");
  404. t->font = _getctlfont("font");
  405. t->selectmode = Selsingle;
  406. t->lastbut = 0;
  407. t->mouse = textmouse;
  408. t->ctl = textctl;
  409. t->exit = textfree;
  410. return (Control *)t;
  411. }