jcs
/amend
/amendments
/15
browser+committer: Enable TETab on all TextEdits
Fix some logic errors in UpdateScrollbarForTE
    jcs made amendment 15 over 4 years ago
--- browser.c	Mon Oct 18 13:09:06 2021
+++ browser.c	Thu Oct 21 17:37:32 2021
@@ -23,6 +23,7 @@
 #include "committer.h"
 #include "diff.h"
 #include "repo.h"
+#include "tetab.h"
 #include "util.h"
 
 void browser_add_files(struct browser *browser);
@@ -144,12 +145,11 @@ browser_init(struct repo *repo)
 	bounds.left = padding;
 	bounds.right = browser->win->portRect.right - SCROLLBAR_WIDTH - padding;
 	bounds.bottom = browser->win->portRect.bottom - padding;
-	TextFont(monaco);
-	TextSize(9);
 	te_bounds = bounds;
 	InsetRect(&te_bounds, 2, 2);
-	browser->diff_te = TENew(&te_bounds, &bounds);
+	browser->diff_te = TEStylNew(&te_bounds, &bounds);
 	TEAutoView(true, browser->diff_te);
+	TETabEnable(browser->diff_te);
 
 	/* scrollbar for diff text */
 	bounds.right = browser->win->portRect.right - padding;
@@ -325,7 +325,6 @@ browser_show_commit(struct browser *browser, struct re
 		SetCursor(&arrow);
 	}
 	
-	InvalRect(&(*(browser->diff_te))->viewRect);
 	UpdateScrollbarForTE(browser->diff_scroller, browser->diff_te, true);
 }
 
@@ -404,7 +403,6 @@ browser_update(struct browser *browser, EventRecord *e
 	case updateEvt:
 		FillRect(&browser->win->portRect, fill_pattern);
 
-		TextFont(monaco);
 		r = (*(browser->diff_te))->viewRect;
 		FillRect(&r, white);
 		TEUpdate(&r, browser->diff_te);
@@ -563,7 +561,7 @@ browser_mouse_down(struct browser *browser, EventRecor
 		if (adj != 0) {
 			val -= adj;
 			if (control == browser->diff_scroller)
-				TEScroll(0, adj * (*(browser->diff_te))->lineHeight,
+				TEScroll(0, adj * TEGetHeight(0, 0, browser->diff_te),
 				  browser->diff_te);
 			SetCtlValue(control, val);
 		}
--- committer.c	Mon Oct 18 16:47:01 2021
+++ committer.c	Fri Oct 22 10:23:36 2021
@@ -23,6 +23,7 @@
 #include "committer.h"
 #include "diff.h"
 #include "repo.h"
+#include "tetab.h"
 #include "util.h"
 
 /* needed by diffreg */
@@ -30,10 +31,14 @@ struct stat stb1, stb2;
 long diff_format, diff_context, status = 0;
 char *ifdefname, *diffargs, *label[2], *ignore_pats;
 
-#define DIFF_LINE_SIZE 1024
+#define DIFF_LINE_SIZE 512
 Handle diff_line = NULL;
 size_t diff_line_pos = 0;
 
+#define DIFF_CHUNK_SIZE (1024 * 4)
+Handle diff_chunk = NULL;
+size_t diff_chunk_pos = 0;
+
 static short padding = 10;
 static short diff_too_big = 0;
 
@@ -46,7 +51,8 @@ void committer_generate_diff(struct committer *committ
 void committer_update_menu(struct committer *committer);
 void committer_status(char *format, ...);
 void committer_commit(struct committer *committer);
-void diff_append_line(char *str, size_t len);
+void diff_append_line(char *str, size_t len, bool flush);
+void diff_chunk_write(void);
 void diff_finish(void);
 
 void
@@ -55,6 +61,7 @@ committer_init(struct browser *browser)
 	char title[256] = { 0 };
 	struct committer *committer;
 	Rect bounds = { 0 }, te_bounds = { 0 };
+	TextStyle style;
 	short fh;
 	
 	committer = xmalloczero(sizeof(struct committer));
@@ -82,15 +89,19 @@ committer_init(struct browser *browser)
 	bounds.top = padding;
 	bounds.left = 50;
 	fh = FontHeight(monaco, 9);
-	bounds.bottom = bounds.top + 2 + (fh * 5) + 2;
+	bounds.bottom = bounds.top + (fh * 5) + 2;
 	bounds.right = committer->win->portRect.right - SCROLLBAR_WIDTH -
 	  padding;
-	TextFont(monaco);
-	TextSize(9);
 	te_bounds = bounds;
 	InsetRect(&te_bounds, 2, 2);
-	committer->log_te = TENew(&te_bounds, &bounds);
+	TextFont(monaco);
+	TextSize(9);
+	committer->log_te = TEStylNew(&te_bounds, &bounds);
+	style.tsFont = monaco;
+	style.tsSize = 9;
+	TESetStyle(doFont | doSize, &style, false, committer->log_te);
 	TEAutoView(true, committer->log_te);
+	TETabEnable(committer->log_te);
 
 	/* scrollbar for log message */
 	bounds.left = bounds.right;
@@ -107,12 +118,11 @@ committer_init(struct browser *browser)
 	  padding;
 	bounds.right = committer->win->portRect.right - SCROLLBAR_WIDTH -
 	  padding;
-	TextFont(monaco);
-	TextSize(9);
 	te_bounds = bounds;
 	InsetRect(&te_bounds, 2, 2);
-	committer->diff_te = TENew(&te_bounds, &bounds);
+	committer->diff_te = TEStylNew(&te_bounds, &bounds);
 	TEAutoView(true, committer->diff_te);
+	TETabEnable(committer->diff_te);
 
 	/* scrollbar for diff */
 	bounds.left = bounds.right;
@@ -153,10 +163,13 @@ committer_close(struct committer *committer)
 	DisposeWindow(committer->win);
 	TEDispose(committer->log_te);
 	DisposHandle(committer->log_scroller);
-	TEDispose(committer->diff_te);
 	DisposHandle(committer->diff_scroller);
 	DisposHandle(committer->commit_button);
 
+	/* in case the hText got committed as a resource */
+	ReleaseResource((*(committer->log_te))->hText);
+	TEDispose(committer->diff_te);
+	
 	free(committer);
 }
 
@@ -179,14 +192,12 @@ committer_update(struct committer *committer, EventRec
 	switch (what) {
 	case -1:
 	case updateEvt:
+		r = (*(committer->log_te))->viewRect;
+		MoveTo(r.top, r.top + FontHeight(applFont, 11) - 2);
 		TextFont(applFont);
 		TextSize(11);
-
-		r = (*(committer->log_te))->viewRect;
-		MoveTo(r.top, r.top + FontHeight(applFont, 10) - 2);
 		DrawText("Log:", 0, 4);
 		
-		TextFont(monaco);
 		r = (*(committer->log_te))->viewRect;
 		TEUpdate(&r, committer->log_te);
 		InsetRect(&r, -1, -1);
@@ -228,7 +239,8 @@ committer_key_down(struct committer *committer, EventR
 	
 	k = (event->message & charCodeMask);
 	TEKey(k, committer->log_te);
-	UpdateScrollbarForTE(committer->log_scroller, committer->log_te, false);
+	UpdateScrollbarForTE(committer->log_scroller, committer->log_te,
+	  false);
 	committer_update_menu(committer);
 }
 
@@ -287,10 +299,10 @@ committer_mouse_down(struct committer *committer, Even
 		if (adj != 0) {
 			val -= adj;
 			if (control == committer->diff_scroller)
-				TEScroll(0, adj * (*(committer->diff_te))->lineHeight,
+				TEScroll(0, adj * TEGetHeight(0, 0, committer->diff_te),
 				  committer->diff_te);
 			else if (control == committer->log_scroller)
-				TEScroll(0, adj * (*(committer->log_te))->lineHeight,
+				TEScroll(0, adj * TEGetHeight(0, 0, committer->log_te),
 				  committer->log_te);
 			SetCtlValue(control, val);
 		}
@@ -335,6 +347,7 @@ committer_generate_diff(struct committer *committer)
 	short i;
 	short *selected_files = NULL;
 	short nselected_files = 0;
+	TextStyle style;
 
 	SetCursor(*(GetCursor(watchCursor)));
 	
@@ -355,6 +368,10 @@ committer_generate_diff(struct committer *committer)
 
 	HLock(committer->diff_te);
 	
+	style.tsFont = monaco;
+	style.tsSize = 9;
+	TESetStyle(doFont | doSize, &style, false, cur_committer->diff_te);
+
 	for (i = 0; i < nselected_files; i++) {
 		file = repo_file_with_id(committer->browser->repo,
 		  selected_files[i]);
@@ -369,7 +386,8 @@ committer_generate_diff(struct committer *committer)
 	
 	HUnlock(committer->diff_te);
 	InvalRect(&committer->win->portRect);
-	UpdateScrollbarForTE(committer->diff_scroller, committer->diff_te, true);
+	UpdateScrollbarForTE(committer->diff_scroller, committer->diff_te,
+	  true);
 	cur_committer = NULL;
 
 	committer_status(NULL);
@@ -476,7 +494,8 @@ diff_output(const char *format, ...)
 	for (i = last_pos; i < diff_line_pos; i++) {
 		if (((char *)*diff_line)[i] == '\n') {
 			((char *)*diff_line)[i] = '\r';
-			diff_append_line(*diff_line + last_line, i - last_line + 1);
+			diff_append_line(*diff_line + last_line, i - last_line + 1,
+			  false);
 			last_line = i + 1;
 		}
 	}
@@ -493,37 +512,56 @@ diff_output(const char *format, ...)
 }
 
 void
-diff_append_line(char *str, size_t len)
+diff_append_line(char *str, size_t len, bool flush)
 {
-	unsigned long cur_htext_size;
 	short tabsize;
 
-	cur_htext_size = GetHandleSize((*(cur_committer->diff_te))->hText);
-	if (cur_committer->diff_te_len + len >= cur_htext_size) {
-		SetHandleSize((*(cur_committer->diff_te))->hText,
-		  cur_htext_size + (1024 * 4));
-		if (MemError())
-			err(1, "Out of memory! Can't expand diff TE beyond %lu bytes.",
-			  cur_htext_size);
+	if (diff_chunk == NULL) {
+		diff_chunk = xNewHandle(DIFF_CHUNK_SIZE);
+		diff_chunk_pos = 0;
 	}
 	
+	if (str[0] == '-' && str[1] != '-')
+		cur_committer->diff_subs++;
+	else if (str[0] == '+' && str[1] != '+')
+		cur_committer->diff_adds++;
+
+	if (diff_chunk_pos + len >= DIFF_CHUNK_SIZE)
+		diff_chunk_write();
+		
+	HLock(diff_chunk);
+	memcpy(*diff_chunk + diff_chunk_pos, str, len);
+	HUnlock(diff_chunk);
+	diff_chunk_pos += len;
+	
+	if (flush)
+		diff_chunk_write();
+}
+
+void
+diff_chunk_write(void)
+{
+	HLock(diff_chunk);
+	
+	if (cur_committer->diff_te_len + diff_chunk_pos > MAX_TEXTEDIT_SIZE) {
+		HUnlock((*(cur_committer->diff_te))->hText);
+		SetHandleSize((*(cur_committer->diff_te))->hText,
+		  cur_committer->diff_te_len + diff_chunk_pos);
+		if (MemError())
+			err(1, "Out of memory! Can't expand diff TE by %lu bytes.",
+			  diff_chunk_pos);
 	HLock((*(cur_committer->diff_te))->hText);
 	memcpy(*(*(cur_committer->diff_te))->hText +
-	  cur_committer->diff_te_len, str, len);
+		  cur_committer->diff_te_len, *diff_chunk, diff_chunk_pos);
 	HUnlock((*(cur_committer->diff_te))->hText);
-		
-	cur_committer->diff_te_len += len;
-
-	if (!diff_too_big && TECanAddLine(cur_committer->diff_te, len)) {
-		(*(cur_committer->diff_te))->teLength = cur_committer->diff_te_len;
 	} else {
-		diff_too_big = 1;
+		TEStylInsert(*diff_chunk, diff_chunk_pos, 0,
+		  cur_committer->diff_te);
 	}
+	HUnlock(diff_chunk);
 	
-	if (str[0] == '-' && str[1] != '-')
-		cur_committer->diff_subs++;
-	else if (str[0] == '+' && str[1] != '+')
-		cur_committer->diff_adds++;
+	cur_committer->diff_te_len += diff_chunk_pos;
+	diff_chunk_pos = 0;
 }
 
 void
@@ -531,26 +569,20 @@ diff_finish(void)
 {
 	if (diff_line != NULL) {
 		if (diff_line_pos)
-			diff_append_line(*diff_line, diff_line_pos);
+			diff_append_line(*diff_line, diff_line_pos, true);
 
 		DisposHandle(diff_line);
 		diff_line = NULL;
 	}
 
-#if 0
 	if (diff_chunk != NULL) {
-		if (diff_chunk_pos) {
-			TESetSelect(1024 * 32, 1024 * 32, cur_committer->diff_te);
-			TEInsert((*diff_chunk), diff_chunk_pos,
-			  cur_committer->diff_te);
-		}
-		
-		diff_chunk_pos = 0;
+		if (diff_chunk_pos)
+			diff_chunk_write();
 		DisposHandle(diff_chunk);
 		diff_chunk = NULL;
 	}
-#endif
 
+	HUnlock((*(cur_committer->diff_te))->hText);
 	SetHandleSize((*(cur_committer->diff_te))->hText,
 	  cur_committer->diff_te_len);
 	TECalText(cur_committer->diff_te);
--- repo.c	Mon Oct 18 17:12:17 2021
+++ repo.c	Fri Oct 22 13:10:20 2021
@@ -331,23 +331,33 @@ repo_file_with_id(struct repo *repo, short id)
 void
 repo_show_diff_text(struct repo_commit *commit, TEHandle te)
 {
-	char buf[512];
-	Handle diffh;
+	Handle diffh, allh;
+	TextStyle style;
 	struct tm *ttm = NULL;
-	unsigned long diff_len;
-	short header_len, i, blen;
+	char *buf;
+	char truncbuf[64];
+	unsigned long diff_len, all_len;
+	short header_len, i, blen, height, trunc = 0;
+	unsigned short warn_off;
 	
 	diffh = Get1Resource(REPO_DIFF_RTYPE, commit->id);
 	if (diffh == NULL)
 		err(1, "failed finding DIFF %d", commit->id);
 
-	HLock(diffh);
-	HLock(commit->log);
-
 	diff_len = GetHandleSize(diffh);
+	if (diff_len == 0) {
+		/*
+		 * Not sure why this happens when viewing the diff that was just
+		 * committed.
+		 */
+		ReleaseResource(diffh);
+		diffh = Get1Resource(REPO_DIFF_RTYPE, commit->id);
+		diff_len = GetHandleSize(diffh);
 	if (diff_len == 0)
-		LoadResource(diffh);
+			err(1, "diff still zero bytes");
+	}
 	
+	buf = xmalloc(512);
 	ttm = localtime(&commit->date);
 	header_len = sprintf(buf,
 	  "Author: %s\r"
@@ -357,32 +367,63 @@ repo_show_diff_text(struct repo_commit *commit, TEHand
 	  ttm->tm_year + 1900, ttm->tm_mon + 1, ttm->tm_mday,
 	  ttm->tm_hour, ttm->tm_min, ttm->tm_sec);
 
+	/* copy log, indenting each line */
+	HLock(commit->log);
 	for (i = 0; i < commit->log_len; i++) {
-		buf[header_len++] = (*(commit->log))[i];
+		*(buf + header_len++) = (*(commit->log))[i];
 
 		if ((*(commit->log))[i] == '\r' && i < commit->log_len - 1) {
-			buf[header_len++] = ' ';
-			buf[header_len++] = ' ';
+			*(buf + header_len++) = ' ';
+			*(buf + header_len++) = ' ';
 		}
 	}
-	buf[header_len++] = '\r';
-	buf[header_len++] = '\r';
+	HUnlock(commit->log);
+	*(buf + header_len++) = '\r';
+	*(buf + header_len++) = '\r';
 
-	TESetText("", 0, te);
-	TEAppendFast(buf, header_len, te);
+	all_len = header_len + diff_len;
+	if (all_len >= MAX_TEXTEDIT_SIZE) {
+		all_len = MAX_TEXTEDIT_SIZE;
+		trunc = 1;
+	}
+	allh = xNewHandle(all_len);
 	
-	if (diff_len + header_len > 32767L) {
-		diff_len = 32767L - header_len - strlen(REPO_DIFF_TOO_BIG) - 20;
-		blen = sprintf(buf, REPO_DIFF_TOO_BIG,
-		  GetHandleSize(diffh) - diff_len);
-		TEAppendFast(*diffh, diff_len, te);
-		TEAppendFast(buf, blen, te);
-	} else
-		TEAppendFast(*diffh, diff_len, te);
+	/*
+	 * A more memory-efficient method would be:
+	 *
+	 * DetachResource(diffh);
+	 * SetHandleSize(diffh, all_len);
+	 * memmove(*diffh, *diffh + header_len, all_len - header_len);
+	 * memcpy(*diffh, buf, header_len);
+	 *
+	 * But memmove doesn't support overlapping buffers :/
+	 */
 	 
+	HLock(allh);
+	memcpy(*allh, buf, header_len);
+	free(buf);
+	
+	HLock(diffh);
+	memcpy(*allh + header_len, *diffh, all_len - header_len);
 	ReleaseResource(diffh);
 	
-	TECalText(te);
+	if (trunc) {
+		warn_off = MAX_TEXTEDIT_SIZE - header_len -
+		  strlen(REPO_DIFF_TOO_BIG);
+		blen = sprintf(truncbuf, REPO_DIFF_TOO_BIG,
+		  diff_len - warn_off);
+		memcpy(*allh + MAX_TEXTEDIT_SIZE - blen, truncbuf, blen);
+	}
+
+	TESetText("", 0, te);
+	/* manually reset scroll without TESetSelect(0, 0, te) which redraws */
+	height = (*te)->destRect.bottom - (*te)->destRect.top;
+	(*te)->destRect.top = (*te)->viewRect.top;
+	(*te)->destRect.bottom = (*te)->viewRect.top + height;
+	style.tsFont = monaco;
+	style.tsSize = 9;
+	TESetStyle(doFont | doSize, &style, false, te);
+	TEStylInsert(*allh, all_len, 0, te);
 }
 
 struct repo_file *
--- util.c	Mon Oct 18 13:14:58 2021
+++ util.c	Fri Oct 22 13:02:18 2021
@@ -460,32 +460,42 @@ DrawGrowIconOnly(WindowPtr win)
 void
 UpdateScrollbarForTE(ControlHandle scroller, TEHandle te, bool reset)
 {
-	size_t vlines;
+	size_t vlines, telines;
 	TERec *ter;
-	short vtop;
+	short vtop, lheight;
+	short max, val;
 	
 	HLock(te);
 	ter = *te;
 	
-	vlines = (ter->viewRect.bottom - ter->viewRect.top) / ter->lineHeight;
-	if (vlines >= ter->nLines)
-		vlines = ter->nLines;
-	SetCtlMax(scroller, ter->nLines - vlines + 1);
+#define ceildiv(a,b) ((a / b) + (!!(a % b)))
 	
-	if (reset) {
-		SetCtlValue(scroller, 1);
+	lheight = TEGetHeight(0, 0, te);
+	vlines = (ter->viewRect.bottom - ter->viewRect.top) / lheight;
+	telines = ter->nLines;
+	/* telines is inaccurate if the last line doesn't have any chars */
+	if (telines >= vlines)
+		telines++;
+	max = telines - vlines;
+	if (max < 1)
+		max = 1;
 	
-		/*
-		 * TESetSelect will redraw if TEAutoView is enabled, but only if
-		 * the control was scrolled already 
-		 */
+	if (reset) {
+		val = 1;
 		vtop = (*te)->viewRect.top;
 		TESetSelect(0, 0, te);
-		if (vtop == (*te)->viewRect.top)
-			TEUpdate(&ter->viewRect, te);
-		ValidRect(&ter->viewRect);
-	} else if (GetCtlValue(scroller) > GetCtlMax(scroller))
-		SetCtlValue(scroller, GetCtlMax(scroller));
+	} else {
+		val = (ter->viewRect.top - ter->destRect.top);
+		if (val < 0)
+			val = 1;
+		else {
+			val = ceildiv(val, lheight) + 1;
+			if (val > max)
+				max = val;
+		}
+	}
+	SetCtlMax(scroller, max);
+	SetCtlValue(scroller, val);
 
 	HUnlock(te);
 }
@@ -499,15 +509,16 @@ SetTrackControlTE(TEHandle te)
 pascal void
 TrackMouseDownInControl(ControlHandle control, short part)
 {
-	short page, val, adj;
+	short page, val, adj, lheight;
 	
 	if (track_control_te == NULL)
 		err(1, "TrackMouseDownInControl without SetTrackControlTE");
 	
+	lheight = TEGetHeight(0, 0, track_control_te);
+
 	/* keep 1 line of context between pages */
 	page = (((*track_control_te)->viewRect.bottom -
-	  (*track_control_te)->viewRect.top) /
-	  (*track_control_te)->lineHeight) - 1;
+	  (*track_control_te)->viewRect.top) / lheight) - 1;
 
 	adj = 0;
 	switch (part) {
@@ -533,7 +544,7 @@ TrackMouseDownInControl(ControlHandle control, short p
 	if (adj == 0)
 		return;
 
-	TEScroll(0, -adj * (*track_control_te)->lineHeight, track_control_te);
+	TEScroll(0, -adj * lheight, track_control_te);
 	SetCtlValue(control, val + adj);
 }
 
--- util.h	Mon Oct 18 13:09:59 2021
+++ util.h	Thu Oct 21 21:20:25 2021
@@ -27,6 +27,8 @@
 
 #define SCROLLBAR_WIDTH 16
 
+#define MAX_TEXTEDIT_SIZE 32767L
+
 #define nitems(what) (sizeof((what)) / sizeof((what)[0]))
 
 #ifndef bool