text.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551
  1. /*
  2. * This file is part of the UCB release of Plan 9. It is subject to the license
  3. * terms in the LICENSE file found in the top-level directory of this
  4. * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
  5. * part of the UCB release of Plan 9, including this file, may be copied,
  6. * modified, propagated, or distributed except according to the terms contained
  7. * in the LICENSE file.
  8. */
  9. #include <u.h>
  10. #include <libc.h>
  11. #include <draw.h>
  12. #include <thread.h>
  13. #include <mouse.h>
  14. #include <keyboard.h>
  15. #include <control.h>
  16. static int debug = 0;
  17. typedef struct Text Text;
  18. struct Text
  19. {
  20. Control;
  21. int border;
  22. int topline;
  23. int scroll;
  24. int nvis;
  25. int lastbut;
  26. CFont *font;
  27. CImage *image;
  28. CImage *textcolor;
  29. CImage *bordercolor;
  30. CImage *selectcolor;
  31. CImage *selectingcolor;
  32. Rune **line;
  33. int selectmode; // Selsingle, Selmulti
  34. int selectstyle; // Seldown, Selup (use Selup only with Selsingle)
  35. uint8_t *selected;
  36. int nline;
  37. int warp;
  38. int align;
  39. int sel; // line nr of selection made by last button down
  40. int but; // last button down (still being hold)
  41. int offsel; // we are on selection
  42. };
  43. enum
  44. {
  45. Selsingle,
  46. Selmulti,
  47. Seldown,
  48. Selup,
  49. };
  50. enum{
  51. EAccumulate,
  52. EAdd,
  53. EAlign,
  54. EBorder,
  55. EBordercolor,
  56. EClear,
  57. EDelete,
  58. EFocus,
  59. EFont,
  60. EHide,
  61. EImage,
  62. ERect,
  63. EReplace,
  64. EReveal,
  65. EScroll,
  66. ESelect,
  67. ESelectcolor,
  68. ESelectingcolor,
  69. ESelectmode,
  70. ESelectstyle,
  71. EShow,
  72. ESize,
  73. ETextcolor,
  74. ETopline,
  75. EValue,
  76. EWarp,
  77. };
  78. static char *cmds[] = {
  79. [EAccumulate] = "accumulate",
  80. [EAdd] = "add",
  81. [EAlign] = "align",
  82. [EBorder] = "border",
  83. [EBordercolor] = "bordercolor",
  84. [EClear] = "clear",
  85. [EDelete] = "delete",
  86. [EFocus] = "focus",
  87. [EFont] = "font",
  88. [EHide] = "hide",
  89. [EImage] = "image",
  90. [ERect] = "rect",
  91. [EReplace] = "replace",
  92. [EReveal] = "reveal",
  93. [EScroll] = "scroll",
  94. [ESelect] = "select",
  95. [ESelectcolor] = "selectcolor",
  96. [ESelectingcolor] = "selectingcolor",
  97. [ESelectmode] = "selectmode",
  98. [ESelectstyle] = "selectstyle",
  99. [EShow] = "show",
  100. [ESize] = "size",
  101. [ETextcolor] = "textcolor",
  102. [ETopline] = "topline",
  103. [EValue] = "value",
  104. [EWarp] = "warp",
  105. nil
  106. };
  107. static void textshow(Text*);
  108. static void texttogglei(Text*, int);
  109. static int textline(Text*, Point);
  110. static int texttoggle(Text*, Point);
  111. static void
  112. textmouse(Control *c, Mouse *m)
  113. {
  114. Text *t;
  115. int sel;
  116. t = (Text*)c;
  117. if (debug) fprint(2, "textmouse %s t->lastbut %d; m->buttons %d\n", t->name, t->lastbut, m->buttons);
  118. if (t->warp >= 0)
  119. return;
  120. if ((t->selectstyle == Selup) && (m->buttons&7)) {
  121. sel = textline(t, m->xy);
  122. if (t->sel >= 0) {
  123. // if (debug) fprint(2, "textmouse Selup %q sel=%d t->sel=%d t->but=%d\n",
  124. // t->name, sel, t->sel, t->but);
  125. t->offsel = (sel == t->sel) ? 0 : 1;
  126. if ((sel == t->sel &&
  127. ((t->selected[t->sel] && !t->but) ||
  128. ((!t->selected[t->sel]) && t->but))) ||
  129. (sel != t->sel &&
  130. ((t->selected[t->sel] && t->but) ||
  131. ((!t->selected[t->sel]) && (!t->but))))) {
  132. texttogglei(t, t->sel);
  133. }
  134. }
  135. }
  136. if(t->lastbut != (m->buttons&7)){
  137. if(m->buttons & 7){
  138. sel = texttoggle(t, m->xy);
  139. if(sel >= 0) {
  140. if (t->selectstyle == Seldown) {
  141. chanprint(t->event, "%q: select %d %d",
  142. t->name, sel, t->selected[sel] ? (m->buttons & 7) : 0);
  143. if (debug) fprint(2, "textmouse Seldown event %q: select %d %d\n",
  144. t->name, sel, t->selected[sel] ? (m->buttons & 7) : 0);
  145. } else {
  146. if (debug) fprint(2, "textmouse Selup no event yet %q: select %d %d\n",
  147. t->name, sel, t->selected[sel] ? (m->buttons & 7) : 0);
  148. t->sel = sel;
  149. t->but = t->selected[sel] ? (m->buttons & 7) : 0;
  150. }
  151. }
  152. } else if (t->selectstyle == Selup) {
  153. sel = textline(t, m->xy);
  154. t->offsel = 0;
  155. if ((sel >= 0) && (sel == t->sel)) {
  156. chanprint(t->event, "%q: select %d %d",
  157. t->name, sel, t->but);
  158. if (debug) fprint(2, "textmouse Selup event %q: select %d %d\n",
  159. t->name, sel, t->but);
  160. } else if (sel != t->sel) {
  161. if ((t->selected[t->sel] && t->but) ||
  162. ((!t->selected[t->sel]) && (!t->but))) {
  163. texttogglei(t, t->sel);
  164. } else {
  165. textshow(t);
  166. }
  167. if (debug) fprint(2, "textmouse Selup cancel %q: select %d %d\n",
  168. t->name, sel, t->but);
  169. }
  170. t->sel = -1;
  171. t->but = 0;
  172. }
  173. t->lastbut = m->buttons & 7;
  174. }
  175. }
  176. static void
  177. textfree(Control *c)
  178. {
  179. int i;
  180. Text *t;
  181. t = (Text*)c;
  182. _putctlfont(t->font);
  183. _putctlimage(t->image);
  184. _putctlimage(t->textcolor);
  185. _putctlimage(t->bordercolor);
  186. _putctlimage(t->selectcolor);
  187. _putctlimage(t->selectingcolor);
  188. for(i=0; i<t->nline; i++)
  189. free(t->line[i]);
  190. free(t->line);
  191. free(t->selected);
  192. }
  193. static void
  194. textshow(Text *t)
  195. {
  196. Rectangle r, tr;
  197. Point p;
  198. int i, ntext;
  199. Font *f;
  200. Rune *text;
  201. if (t->hidden)
  202. return;
  203. r = t->rect;
  204. f = t->font->font;
  205. draw(t->screen, r, t->image->image, nil, t->image->image->r.min);
  206. if(t->border > 0){
  207. border(t->screen, r, t->border, t->bordercolor->image, t->bordercolor->image->r.min);
  208. r = insetrect(r, t->border);
  209. }
  210. tr = r;
  211. t->nvis = Dy(r)/f->height;
  212. for(i=t->topline; i<t->nline && i<t->topline+t->nvis; i++){
  213. text = t->line[i];
  214. ntext = runestrlen(text);
  215. r.max.y = r.min.y+f->height;
  216. if(t->sel == i && t->offsel)
  217. draw(t->screen, r, t->selectingcolor->image, nil, ZP);
  218. else if(t->selected[i])
  219. draw(t->screen, r, t->selectcolor->image, nil, ZP);
  220. p = _ctlalignpoint(r,
  221. runestringnwidth(f, text, ntext),
  222. f->height, t->align);
  223. if(t->warp == i) {
  224. Point p2;
  225. p2.x = p.x + 0.5*runestringnwidth(f, text, ntext);
  226. p2.y = p.y + 0.5*f->height;
  227. moveto(t->controlset->mousectl, p2);
  228. t->warp = -1;
  229. }
  230. _string(t->screen, p, t->textcolor->image,
  231. ZP, f, nil, text, ntext, tr,
  232. nil, ZP, SoverD);
  233. r.min.y += f->height;
  234. }
  235. flushimage(display, 1);
  236. }
  237. static void
  238. textctl(Control *c, CParse *cp)
  239. {
  240. int cmd, i, n;
  241. Rectangle r;
  242. Text *t;
  243. Rune *rp;
  244. t = (Text*)c;
  245. cmd = _ctllookup(cp->args[0], cmds, nelem(cmds));
  246. switch(cmd){
  247. default:
  248. ctlerror("%q: unrecognized message '%s'", t->name, cp->str);
  249. break;
  250. case EAlign:
  251. _ctlargcount(t, cp, 2);
  252. t->align = _ctlalignment(cp->args[1]);
  253. break;
  254. case EBorder:
  255. _ctlargcount(t, cp, 2);
  256. if(cp->iargs[1] < 0)
  257. ctlerror("%q: bad border: %c", t->name, cp->str);
  258. t->border = cp->iargs[1];
  259. break;
  260. case EBordercolor:
  261. _ctlargcount(t, cp, 2);
  262. _setctlimage(t, &t->bordercolor, cp->args[1]);
  263. break;
  264. case EClear:
  265. _ctlargcount(t, cp, 1);
  266. for(i=0; i<t->nline; i++)
  267. free(t->line[i]);
  268. free(t->line);
  269. free(t->selected);
  270. t->line = ctlmalloc(sizeof(Rune*));
  271. t->selected = ctlmalloc(1);
  272. t->nline = 0;
  273. textshow(t);
  274. break;
  275. case EDelete:
  276. _ctlargcount(t, cp, 2);
  277. i = cp->iargs[1];
  278. if(i<0 || i>=t->nline)
  279. ctlerror("%q: line number out of range: %s", t->name, cp->str);
  280. free(t->line[i]);
  281. memmove(t->line+i, t->line+i+1, (t->nline-(i+1))*sizeof(Rune*));
  282. memmove(t->selected+i, t->selected+i+1, t->nline-(i+1));
  283. t->nline--;
  284. textshow(t);
  285. break;
  286. case EFocus:
  287. break;
  288. case EFont:
  289. _ctlargcount(t, cp, 2);
  290. _setctlfont(t, &t->font, cp->args[1]);
  291. break;
  292. case EHide:
  293. _ctlargcount(t, cp, 1);
  294. t->hidden = 1;
  295. break;
  296. case EImage:
  297. _ctlargcount(t, cp, 2);
  298. _setctlimage(t, &t->image, cp->args[1]);
  299. break;
  300. case ERect:
  301. _ctlargcount(t, cp, 5);
  302. r.min.x = cp->iargs[1];
  303. r.min.y = cp->iargs[2];
  304. r.max.x = cp->iargs[3];
  305. r.max.y = cp->iargs[4];
  306. if(Dx(r)<=0 || Dy(r)<=0)
  307. ctlerror("%q: bad rectangle: %s", t->name, cp->str);
  308. t->rect = r;
  309. t->nvis = (Dy(r)-2*t->border)/t->font->font->height;
  310. break;
  311. case EReplace:
  312. _ctlargcount(t, cp, 3);
  313. i = cp->iargs[1];
  314. if(i<0 || i>=t->nline)
  315. ctlerror("%q: line number out of range: %s", t->name, cp->str);
  316. free(t->line[i]);
  317. t->line[i] = _ctlrunestr(cp->args[2]);
  318. textshow(t);
  319. break;
  320. case EReveal:
  321. _ctlargcount(t, cp, 1);
  322. t->hidden = 0;
  323. textshow(t);
  324. break;
  325. case EScroll:
  326. _ctlargcount(t, cp, 2);
  327. t->scroll = cp->iargs[1];
  328. break;
  329. case ESelect:
  330. if(cp->nargs!=2 && cp->nargs!=3)
  331. badselect:
  332. ctlerror("%q: bad select message: %s", t->name, cp->str);
  333. if(cp->nargs == 2){
  334. if(strcmp(cp->args[1], "all") == 0){
  335. memset(t->selected, 1, t->nline);
  336. break;
  337. }
  338. if(strcmp(cp->args[1], "none") == 0){
  339. memset(t->selected, 0, t->nline);
  340. break;
  341. }
  342. if(cp->args[1][0]<'0' && '9'<cp->args[1][0])
  343. goto badselect;
  344. texttogglei(t, cp->iargs[1]);
  345. break;
  346. }
  347. if(cp->iargs[1]<0 || cp->iargs[1]>=t->nline)
  348. ctlerror("%q: selection index out of range (nline %d): %s", t->name, t->nline, cp->str);
  349. if(t->selected[cp->iargs[1]] != (cp->iargs[2]!=0))
  350. texttogglei(t, cp->iargs[1]);
  351. break;
  352. case ESelectcolor:
  353. _ctlargcount(t, cp, 2);
  354. _setctlimage(t, &t->selectcolor, cp->args[1]);
  355. break;
  356. case ESelectmode:
  357. _ctlargcount(t, cp, 2);
  358. if(strcmp(cp->args[1], "single") == 0)
  359. t->selectmode = Selsingle;
  360. else if(strncmp(cp->args[1], "multi", 5) == 0)
  361. t->selectmode = Selmulti;
  362. break;
  363. case ESelectstyle:
  364. _ctlargcount(t, cp, 2);
  365. if(strcmp(cp->args[1], "down") == 0)
  366. t->selectstyle = Seldown;
  367. else if(strcmp(cp->args[1], "up") == 0)
  368. t->selectstyle = Selup;
  369. break;
  370. case EShow:
  371. _ctlargcount(t, cp, 1);
  372. textshow(t);
  373. break;
  374. case ESize:
  375. if (cp->nargs == 3)
  376. r.max = Pt(10000, 10000);
  377. else{
  378. _ctlargcount(t, cp, 5);
  379. r.max.x = cp->iargs[3];
  380. r.max.y = cp->iargs[4];
  381. }
  382. r.min.x = cp->iargs[1];
  383. r.min.y = cp->iargs[2];
  384. 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)
  385. ctlerror("%q: bad sizes: %s", t->name, cp->str);
  386. t->size.min = r.min;
  387. t->size.max = r.max;
  388. break;
  389. case ETextcolor:
  390. _ctlargcount(t, cp, 2);
  391. _setctlimage(t, &t->textcolor, cp->args[1]);
  392. break;
  393. case ETopline:
  394. _ctlargcount(t, cp, 2);
  395. i = cp->iargs[1];
  396. if(i < 0)
  397. i = 0;
  398. if(i > t->nline)
  399. i = t->nline;
  400. if(t->topline != i){
  401. t->topline = i;
  402. textshow(t);
  403. }
  404. break;
  405. case EValue:
  406. /* set contents to single line */
  407. /* free existing text and fall through to add */
  408. for(i=0; i<t->nline; i++){
  409. free(t->line[i]);
  410. t->line[i] = nil;
  411. }
  412. t->nline = 0;
  413. t->topline = 0;
  414. /* fall through */
  415. case EAccumulate:
  416. case EAdd:
  417. switch (cp->nargs) {
  418. default:
  419. ctlerror("%q: wrong argument count in '%s'", t->name, cp->str);
  420. case 2:
  421. n = t->nline;
  422. break;
  423. case 3:
  424. n = cp->iargs[1];
  425. if(n<0 || n>t->nline)
  426. ctlerror("%q: line number out of range: %s", t->name, cp->str);
  427. break;
  428. }
  429. rp = _ctlrunestr(cp->args[cp->nargs-1]);
  430. t->line = ctlrealloc(t->line, (t->nline+1)*sizeof(Rune*));
  431. memmove(t->line+n+1, t->line+n, (t->nline-n)*sizeof(Rune*));
  432. t->line[n] = rp;
  433. t->selected = ctlrealloc(t->selected, t->nline+1);
  434. memmove(t->selected+n+1, t->selected+n, t->nline-n);
  435. t->selected[n] = (t->selectmode==Selmulti && cmd!=EAccumulate);
  436. t->nline++;
  437. if(t->scroll) {
  438. if(n > t->topline + (t->nvis - 1)){
  439. t->topline = n - (t->nvis - 1);
  440. if(t->topline < 0)
  441. t->topline = 0;
  442. }
  443. if(n < t->topline)
  444. t->topline = n;
  445. }
  446. if(cmd != EAccumulate)
  447. if(t->scroll || t->nline<=t->topline+t->nvis)
  448. textshow(t);
  449. break;
  450. case EWarp:
  451. _ctlargcount(t, cp, 2);
  452. i = cp->iargs[1];
  453. if(i <0 || i>=t->nline)
  454. ctlerror("%q: selection index out of range (nline %d): %s", t->name, t->nline, cp->str);
  455. if(i < t->topline || i >= t->topline+t->nvis){
  456. t->topline = i;
  457. }
  458. t->warp = cp->iargs[1];
  459. textshow(t);
  460. t->warp = -1;
  461. break;
  462. }
  463. }
  464. static void
  465. texttogglei(Text *t, int i)
  466. {
  467. int prev;
  468. if(t->selectmode == Selsingle){
  469. /* clear the others */
  470. prev = t->selected[i];
  471. memset(t->selected, 0, t->nline);
  472. t->selected[i] = prev;
  473. }
  474. t->selected[i] ^= 1;
  475. textshow(t);
  476. }
  477. static int
  478. textline(Text *t, Point p)
  479. {
  480. Rectangle r;
  481. int i;
  482. r = t->rect;
  483. if(t->border > 0)
  484. r = insetrect(r, t->border);
  485. if(!ptinrect(p, r))
  486. return -1;
  487. i = (p.y-r.min.y)/t->font->font->height;
  488. i += t->topline;
  489. if(i >= t->nline)
  490. return -1;
  491. return i;
  492. }
  493. static int
  494. texttoggle(Text *t, Point p)
  495. {
  496. int i;
  497. i = textline(t, p);
  498. if (i >= 0)
  499. texttogglei(t, i);
  500. return i;
  501. }
  502. Control*
  503. createtext(Controlset *cs, char *name)
  504. {
  505. Text *t;
  506. t = (Text*)_createctl(cs, "text", sizeof(Text), name);
  507. t->line = ctlmalloc(sizeof(Rune*));
  508. t->selected = ctlmalloc(1);
  509. t->nline = 0;
  510. t->image = _getctlimage("white");
  511. t->textcolor = _getctlimage("black");
  512. t->bordercolor = _getctlimage("black");
  513. t->selectcolor = _getctlimage("yellow");
  514. t->selectingcolor = _getctlimage("paleyellow");
  515. t->font = _getctlfont("font");
  516. t->selectmode = Selsingle;
  517. t->selectstyle = Selup; // Seldown;
  518. t->lastbut = 0;
  519. t->mouse = textmouse;
  520. t->ctl = textctl;
  521. t->exit = textfree;
  522. t->warp = -1;
  523. t->sel = -1;
  524. t->offsel = 0;
  525. t->but = 0;
  526. return (Control *)t;
  527. }