wind.b 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558
  1. implement Windowm;
  2. include "common.m";
  3. sys : Sys;
  4. utils : Utils;
  5. drawm : Draw;
  6. graph : Graph;
  7. gui : Gui;
  8. dat : Dat;
  9. bufferm : Bufferm;
  10. textm : Textm;
  11. filem : Filem;
  12. look : Look;
  13. scrl : Scroll;
  14. acme : Acme;
  15. sprint : import sys;
  16. FALSE, TRUE, XXX, Astring : import Dat;
  17. Reffont, reffont, Lock, Ref, button, modbutton : import dat;
  18. Point, Rect, Image : import drawm;
  19. min, max, error, warning, stralloc, strfree : import utils;
  20. font, draw : import graph;
  21. black, white, mainwin : import gui;
  22. Buffer : import bufferm;
  23. Body, Text, Tag : import textm;
  24. File : import filem;
  25. Xfid : import Xfidm;
  26. scrdraw : import scrl;
  27. tagcols, textcols : import acme;
  28. BORD : import Framem;
  29. init(mods : ref Dat->Mods)
  30. {
  31. sys = mods.sys;
  32. dat = mods.dat;
  33. utils = mods.utils;
  34. drawm = mods.draw;
  35. graph = mods.graph;
  36. gui = mods.gui;
  37. textm = mods.textm;
  38. filem = mods.filem;
  39. bufferm = mods.bufferm;
  40. look = mods.look;
  41. scrl = mods.scroll;
  42. acme = mods.acme;
  43. }
  44. winid : int;
  45. nullwin : Window;
  46. Window.init(w : self ref Window, clone : ref Window, r : Rect)
  47. {
  48. r1, br : Rect;
  49. f : ref File;
  50. rf : ref Reffont;
  51. rp : ref Astring;
  52. nc : int;
  53. dummy : ref File = nil;
  54. c := w.col;
  55. *w = nullwin;
  56. w.col = c;
  57. w.nopen = array[Dat->QMAX] of byte;
  58. for (i := 0; i < Dat->QMAX; i++)
  59. w.nopen[i] = byte 0;
  60. w.qlock = Lock.init();
  61. w.ctllock = Lock.init();
  62. w.refx = Ref.init();
  63. w.tag = textm->newtext();
  64. w.tag.w = w;
  65. w.body = textm->newtext();
  66. w.body.w = w;
  67. w.id = ++winid;
  68. w.refx.inc();
  69. w.ctlfid = ~0;
  70. w.utflastqid = -1;
  71. r1 = r;
  72. r1.max.y = r1.min.y + font.height;
  73. reffont.r.inc();
  74. f = dummy.addtext(w.tag);
  75. w.tag.init(f, r1, reffont, tagcols);
  76. w.tag.what = Tag;
  77. # tag is a copy of the contents, not a tracked image
  78. if(clone != nil){
  79. w.tag.delete(0, w.tag.file.buf.nc, TRUE);
  80. nc = clone.tag.file.buf.nc;
  81. rp = utils->stralloc(nc);
  82. clone.tag.file.buf.read(0, rp, 0, nc);
  83. w.tag.insert(0, rp.s, nc, TRUE, 0);
  84. utils->strfree(rp);
  85. rp = nil;
  86. w.tag.file.reset();
  87. w.tag.setselect(nc, nc);
  88. }
  89. r1 = r;
  90. r1.min.y += font.height + 1;
  91. if(r1.max.y < r1.min.y)
  92. r1.max.y = r1.min.y;
  93. f = nil;
  94. if(clone != nil){
  95. f = clone.body.file;
  96. w.body.org = clone.body.org;
  97. w.isscratch = clone.isscratch;
  98. rf = Reffont.get(FALSE, FALSE, FALSE, clone.body.reffont.f.name);
  99. }else
  100. rf = Reffont.get(FALSE, FALSE, FALSE, nil);
  101. f = f.addtext(w.body);
  102. w.body.what = Body;
  103. w.body.init(f, r1, rf, textcols);
  104. r1.min.y -= 1;
  105. r1.max.y = r1.min.y+1;
  106. draw(mainwin, r1, tagcols[BORD], nil, (0, 0));
  107. scrdraw(w.body);
  108. w.r = r;
  109. w.r.max.y = w.body.frame.r.max.y;
  110. br.min = w.tag.scrollr.min;
  111. br.max.x = br.min.x + button.r.dx();
  112. br.max.y = br.min.y + button.r.dy();
  113. draw(mainwin, br, button, nil, button.r.min);
  114. w.filemenu = TRUE;
  115. w.maxlines = w.body.frame.maxlines;
  116. if(clone != nil){
  117. w.dirty = clone.dirty;
  118. w.body.setselect(clone.body.q0, clone.body.q1);
  119. w.settag();
  120. }
  121. }
  122. Window.reshape(w : self ref Window, r : Rect, safe : int) : int
  123. {
  124. r1, br : Rect;
  125. y : int;
  126. b : ref Image;
  127. r1 = r;
  128. r1.max.y = r1.min.y + font.height;
  129. y = r1.max.y;
  130. if(!safe || !w.tag.frame.r.eq(r1)){
  131. y = w.tag.reshape(r1);
  132. b = button;
  133. if(w.body.file.mod && !w.isdir && !w.isscratch)
  134. b = modbutton;
  135. br.min = w.tag.scrollr.min;
  136. br.max.x = br.min.x + b.r.dx();
  137. br.max.y = br.min.y + b.r.dy();
  138. draw(mainwin, br, b, nil, b.r.min);
  139. }
  140. if(!safe || !w.body.frame.r.eq(r1)){
  141. if(y+1+font.height > r.max.y){ # no body
  142. r1.min.y = y;
  143. r1.max.y = y;
  144. w.body.reshape(r1);
  145. w.r = r;
  146. w.r.max.y = y;
  147. return y;
  148. }
  149. r1 = r;
  150. r1.min.y = y;
  151. r1.max.y = y + 1;
  152. draw(mainwin, r1, tagcols[BORD], nil, (0, 0));
  153. r1.min.y = y + 1;
  154. r1.max.y = r.max.y;
  155. y = w.body.reshape(r1);
  156. w.r = r;
  157. w.r.max.y = y;
  158. scrdraw(w.body);
  159. }
  160. w.maxlines = min(w.body.frame.nlines, max(w.maxlines, w.body.frame.maxlines));
  161. return w.r.max.y;
  162. }
  163. Window.lock1(w : self ref Window, owner : int)
  164. {
  165. w.refx.inc();
  166. w.qlock.lock();
  167. w.owner = owner;
  168. }
  169. Window.lock(w : self ref Window, owner : int)
  170. {
  171. i : int;
  172. f : ref File;
  173. f = w.body.file;
  174. for(i=0; i<f.ntext; i++)
  175. f.text[i].w.lock1(owner);
  176. }
  177. Window.unlock(w : self ref Window)
  178. {
  179. f := w.body.file;
  180. #
  181. # subtle: loop runs backwards to avoid tripping over
  182. # winclose indirectly editing f.text and freeing f
  183. # on the last iteration of the loop
  184. #
  185. for(i:=f.ntext-1; i>=0; i--){
  186. w = f.text[i].w;
  187. w.owner = 0;
  188. w.qlock.unlock();
  189. w.close();
  190. }
  191. }
  192. Window.mousebut(w : self ref Window)
  193. {
  194. graph->cursorset(w.tag.scrollr.min.add(w.tag.scrollr.max).div(2));
  195. }
  196. Window.dirfree(w : self ref Window)
  197. {
  198. i : int;
  199. dl : ref Dat->Dirlist;
  200. if(w.isdir){
  201. for(i=0; i<w.ndl; i++){
  202. dl = w.dlp[i];
  203. dl.r = nil;
  204. dl = nil;
  205. }
  206. }
  207. w.dlp = nil;
  208. w.ndl = 0;
  209. }
  210. Window.close(w : self ref Window)
  211. {
  212. i : int;
  213. if(w.refx.dec() == 0){
  214. w.dirfree();
  215. w.tag.close();
  216. w.body.close();
  217. if(dat->activewin == w)
  218. dat->activewin = nil;
  219. for(i=0; i<w.nincl; i++)
  220. w.incl[i] = nil;
  221. w.incl = nil;
  222. w.events = nil;
  223. w = nil;
  224. }
  225. }
  226. Window.delete(w : self ref Window)
  227. {
  228. x : ref Xfid;
  229. x = w.eventx;
  230. if(x != nil){
  231. w.nevents = 0;
  232. w.events = nil;
  233. w.eventx = nil;
  234. x.c <-= Xfidm->Xnil;
  235. }
  236. }
  237. Window.undo(w : self ref Window, isundo : int)
  238. {
  239. body : ref Text;
  240. i : int;
  241. f : ref File;
  242. v : ref Window;
  243. if(w==nil)
  244. return;
  245. w.utflastqid = -1;
  246. body = w.body;
  247. (body.q0, body.q1) = body.file.undo(isundo, body.q0, body.q1);
  248. body.show(body.q0, body.q1, TRUE);
  249. f = body.file;
  250. for(i=0; i<f.ntext; i++){
  251. v = f.text[i].w;
  252. v.dirty = (f.seq != v.putseq);
  253. if(v != w){
  254. v.body.q0 = v.body.frame.p0+v.body.org;
  255. v.body.q1 = v.body.frame.p1+v.body.org;
  256. }
  257. }
  258. w.settag();
  259. }
  260. Window.setname(w : self ref Window, name : string, n : int)
  261. {
  262. t : ref Text;
  263. v : ref Window;
  264. i : int;
  265. t = w.body;
  266. if(t.file.name == name)
  267. return;
  268. w.isscratch = FALSE;
  269. if(n>=6 && name[n-6:n] == "/guide")
  270. w.isscratch = TRUE;
  271. else if(n>=7 && name[n-7:n] == "+Errors")
  272. w.isscratch = TRUE;
  273. t.file.setname(name, n);
  274. for(i=0; i<t.file.ntext; i++){
  275. v = t.file.text[i].w;
  276. v.settag();
  277. v.isscratch = w.isscratch;
  278. }
  279. }
  280. Window.typex(w : self ref Window, t : ref Text, r : int)
  281. {
  282. i : int;
  283. t.typex(r, w.echomode);
  284. if(t.what == Body)
  285. for(i=0; i<t.file.ntext; i++)
  286. scrdraw(t.file.text[i]);
  287. w.settag();
  288. }
  289. Window.cleartag(w : self ref Window)
  290. {
  291. i, n : int;
  292. r : ref Astring;
  293. # w must be committed
  294. n = w.tag.file.buf.nc;
  295. r = utils->stralloc(n);
  296. w.tag.file.buf.read(0, r, 0, n);
  297. for(i=0; i<n; i++)
  298. if(r.s[i]==' ' || r.s[i]=='\t')
  299. break;
  300. for(; i<n; i++)
  301. if(r.s[i] == '|')
  302. break;
  303. if(i == n)
  304. return;
  305. i++;
  306. w.tag.delete(i, n, TRUE);
  307. utils->strfree(r);
  308. r = nil;
  309. w.tag.file.mod = FALSE;
  310. if(w.tag.q0 > i)
  311. w.tag.q0 = i;
  312. if(w.tag.q1 > i)
  313. w.tag.q1 = i;
  314. w.tag.setselect(w.tag.q0, w.tag.q1);
  315. }
  316. Window.settag(w : self ref Window)
  317. {
  318. i : int;
  319. f : ref File;
  320. f = w.body.file;
  321. for(i=0; i<f.ntext; i++){
  322. v := f.text[i].w;
  323. if(v.col.safe || v.body.frame.maxlines>0)
  324. v.settag1();
  325. }
  326. }
  327. Window.settag1(w : self ref Window)
  328. {
  329. ii, j, k, n, bar, dirty : int;
  330. old : ref Astring;
  331. new : string;
  332. r : int;
  333. b : ref Image;
  334. q0, q1 : int;
  335. br : Rect;
  336. if(w.tag.ncache!=0 || w.tag.file.mod)
  337. w.commit(w.tag); # check file name; also can now modify tag
  338. old = utils->stralloc(w.tag.file.buf.nc);
  339. w.tag.file.buf.read(0, old, 0, w.tag.file.buf.nc);
  340. for(ii=0; ii<w.tag.file.buf.nc; ii++)
  341. if(old.s[ii]==' ' || old.s[ii]=='\t')
  342. break;
  343. if(old.s[0:ii] != w.body.file.name){
  344. w.tag.delete(0, ii, TRUE);
  345. w.tag.insert(0, w.body.file.name, len w.body.file.name, TRUE, 0);
  346. strfree(old);
  347. old = nil;
  348. old = utils->stralloc(w.tag.file.buf.nc);
  349. w.tag.file.buf.read(0, old, 0, w.tag.file.buf.nc);
  350. }
  351. new = w.body.file.name + " Del Snarf";
  352. if(w.filemenu){
  353. if(w.body.file.delta.nc>0 || w.body.ncache)
  354. new += " Undo";
  355. if(w.body.file.epsilon.nc > 0)
  356. new += " Redo";
  357. dirty = w.body.file.name != nil && (w.body.ncache || w.body.file.seq!=w.putseq);
  358. if(!w.isdir && dirty)
  359. new += " Put";
  360. }
  361. if(w.isdir)
  362. new += " Get";
  363. l := len w.body.file.name;
  364. if(l >= 2 && w.body.file.name[l-2: ] == ".b")
  365. new += " Limbo";
  366. new += " |";
  367. r = utils->strchr(old.s, '|');
  368. if(r >= 0)
  369. k = r+1;
  370. else{
  371. k = w.tag.file.buf.nc;
  372. if(w.body.file.seq == 0)
  373. new += " Look ";
  374. }
  375. if(new != old.s[0:k]){
  376. n = k;
  377. if(n > len new)
  378. n = len new;
  379. for(j=0; j<n; j++)
  380. if(old.s[j] != new[j])
  381. break;
  382. q0 = w.tag.q0;
  383. q1 = w.tag.q1;
  384. w.tag.delete(j, k, TRUE);
  385. w.tag.insert(j, new[j:], len new - j, TRUE, 0);
  386. # try to preserve user selection
  387. r = utils->strchr(old.s, '|');
  388. if(r >= 0){
  389. bar = r;
  390. if(q0 > bar){
  391. bar = utils->strchr(new, '|')-bar;
  392. w.tag.q0 = q0+bar;
  393. w.tag.q1 = q1+bar;
  394. }
  395. }
  396. }
  397. strfree(old);
  398. old = nil;
  399. new = nil;
  400. w.tag.file.mod = FALSE;
  401. n = w.tag.file.buf.nc+w.tag.ncache;
  402. if(w.tag.q0 > n)
  403. w.tag.q0 = n;
  404. if(w.tag.q1 > n)
  405. w.tag.q1 = n;
  406. w.tag.setselect(w.tag.q0, w.tag.q1);
  407. b = button;
  408. if(!w.isdir && !w.isscratch && (w.body.file.mod || w.body.ncache))
  409. b = modbutton;
  410. br.min = w.tag.scrollr.min;
  411. br.max.x = br.min.x + b.r.dx();
  412. br.max.y = br.min.y + b.r.dy();
  413. draw(mainwin, br, b, nil, b.r.min);
  414. }
  415. Window.commit(w : self ref Window, t : ref Text)
  416. {
  417. r : ref Astring;
  418. i : int;
  419. f : ref File;
  420. t.commit(TRUE);
  421. f = t.file;
  422. if(f.ntext > 1)
  423. for(i=0; i<f.ntext; i++)
  424. f.text[i].commit(FALSE); # no-op for t
  425. if(t.what == Body)
  426. return;
  427. r = utils->stralloc(w.tag.file.buf.nc);
  428. w.tag.file.buf.read(0, r, 0, w.tag.file.buf.nc);
  429. for(i=0; i<w.tag.file.buf.nc; i++)
  430. if(r.s[i]==' ' || r.s[i]=='\t')
  431. break;
  432. if(r.s[0:i] != w.body.file.name){
  433. dat->seq++;
  434. w.body.file.mark();
  435. w.body.file.mod = TRUE;
  436. w.dirty = TRUE;
  437. w.setname(r.s, i);
  438. w.settag();
  439. }
  440. utils->strfree(r);
  441. r = nil;
  442. }
  443. Window.addincl(w : self ref Window, r : string, n : int)
  444. {
  445. {
  446. (ok, d) := sys->stat(r);
  447. if(ok < 0){
  448. if(r[0] == '/')
  449. raise "e";
  450. (r, n) = look->dirname(w.body, r, n);
  451. (ok, d) = sys->stat(r);
  452. if(ok < 0)
  453. raise "e";
  454. }
  455. if((d.mode&Sys->DMDIR) == 0){
  456. warning(nil, sprint("%s: not a directory\n", r));
  457. r = nil;
  458. return;
  459. }
  460. w.nincl++;
  461. owi := w.incl;
  462. w.incl = array[w.nincl] of string;
  463. w.incl[1:] = owi[0:w.nincl-1];
  464. owi = nil;
  465. w.incl[0] = r;
  466. r = nil;
  467. }
  468. exception{
  469. * =>
  470. warning(nil, sprint("%s: %r\n", r));
  471. r = nil;
  472. }
  473. }
  474. Window.clean(w : self ref Window, conservative : int, exiting : int) : int # as it stands, conservative is always TRUE
  475. {
  476. if(w.isscratch || w.isdir) # don't whine if it's a guide file, error window, etc.
  477. return TRUE;
  478. if((!conservative||exiting) && w.nopen[Dat->QWevent]>byte 0)
  479. return TRUE;
  480. if(w.dirty){
  481. if(w.body.file.name != nil)
  482. warning(nil, sprint("%s modified\n", w.body.file.name));
  483. else{
  484. if(w.body.file.buf.nc < 100) # don't whine if it's too small
  485. return TRUE;
  486. warning(nil, "unnamed file modified\n");
  487. }
  488. w.dirty = FALSE;
  489. return FALSE;
  490. }
  491. return TRUE;
  492. }
  493. Window.ctlprint(w : self ref Window, fonts : int) : string
  494. {
  495. s := sprint("%11d %11d %11d %11d %11d ", w.id, w.tag.file.buf.nc,
  496. w.body.file.buf.nc, w.isdir, w.dirty);
  497. if(fonts)
  498. return sprint("%s%11d %q %11d ", s, w.body.frame.r.dx(),
  499. w.body.reffont.f.name, w.body.frame.maxtab);
  500. return s;
  501. }
  502. Window.event(w : self ref Window, fmt : string)
  503. {
  504. n : int;
  505. x : ref Xfid;
  506. if(w.nopen[Dat->QWevent] == byte 0)
  507. return;
  508. if(w.owner == 0)
  509. error("no window owner");
  510. n = len fmt;
  511. w.events[len w.events] = w.owner;
  512. w.events += fmt;
  513. w.nevents += n+1;
  514. x = w.eventx;
  515. if(x != nil){
  516. w.eventx = nil;
  517. x.c <-= Xfidm->Xnil;
  518. }
  519. }