AmendHub

Download

jcs

/

amend

/

editor.c

 

(View History)

jcs   browser: Limit initial window size to about 81 characters in committer view Latest amendment: 96 on 2022-09-06

1 /*
2 * Copyright (c) 2022 joshua stein <jcs@jcs.org>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include <stdarg.h>
18 #include <stdio.h>
19 #include <string.h>
20
21 #include <time.h>
22 #include "amend.h"
23 #include "browser.h"
24 #include "editor.h"
25 #include "focusable.h"
26 #include "repo.h"
27 #include "tetab.h"
28 #include "util.h"
29
30 #define LABEL_FONT geneva
31 #define LABEL_FONT_SIZE 10
32
33 #define PADDING 10
34
35 bool editor_close(struct focusable *focusable);
36 void editor_idle(struct focusable *focusable, EventRecord *event);
37 void editor_update(struct focusable *focusable, EventRecord *event);
38 void editor_suspend(struct focusable *focusable);
39 void editor_resume(struct focusable *focusable);
40 void editor_key_down(struct focusable *focusable, EventRecord *event);
41 void editor_mouse_down(struct focusable *focusable, EventRecord *event);
42 bool editor_handle_menu(struct focusable *focusable, short menu,
43 short item);
44
45 void editor_update_menu(struct editor *editor);
46 void editor_save(struct editor *editor);
47
48 void
49 editor_init(struct browser *browser, struct repo_amendment *amendment)
50 {
51 Str255 title, filename;
52 struct editor *editor;
53 struct focusable *focusable;
54 char date[32];
55 Rect bounds = { 0 }, te_bounds = { 0 };
56 TextStyle style;
57 short fh, off;
58 struct tm *ttm = NULL;
59
60 editor = xmalloczero(sizeof(struct editor), "editor");
61 editor->browser = browser;
62 editor->amendment = amendment;
63
64 /* main window, centered in its browser */
65 window_rect(browser->win, &bounds);
66 bounds.top += MBarHeight;
67 InsetRect(&bounds, 2, 2);
68 off = (bounds.bottom - bounds.top) / 5;
69 bounds.top += off;
70 bounds.bottom -= off;
71
72 memcpy(filename, browser->repo->bile->filename, sizeof(filename));
73 PtoCstr(filename);
74 snprintf((char *)&title, sizeof(title), "%s: %s: Edit Amendment %d",
75 PROGRAM_NAME, (browser->repo ? (char *)filename : "No repo open"),
76 amendment->id);
77
78 editor->win = NewWindow(0L, &bounds, CtoPstr(title), false,
79 noGrowDocProc, (WindowPtr)-1L, true, 0);
80 if (!editor)
81 err(1, "Can't create editor window");
82 SetPort(editor->win);
83
84 /* author */
85 bounds.top = PADDING;
86 bounds.left = 55;
87 fh = FontHeight(LABEL_FONT, LABEL_FONT_SIZE);
88 bounds.bottom = bounds.top + fh + 4;
89 bounds.right = 140;
90 te_bounds = bounds;
91 InsetRect(&te_bounds, 2, 2);
92 TextFont(LABEL_FONT);
93 TextSize(LABEL_FONT_SIZE);
94 editor->author_te = TENew(&te_bounds, &bounds);
95 TEAutoView(true, editor->author_te);
96 TEActivate(editor->author_te);
97 TEInsert(amendment->author, strlen(amendment->author),
98 editor->author_te);
99
100 /* date */
101 bounds.top = bounds.bottom + PADDING;
102 bounds.bottom = bounds.top + fh + 2;
103 bounds.right = 200;
104 te_bounds = bounds;
105 InsetRect(&te_bounds, 2, 2);
106 TextFont(LABEL_FONT);
107 TextSize(LABEL_FONT_SIZE);
108 editor->date_te = TENew(&te_bounds, &bounds);
109 TEAutoView(true, editor->date_te);
110
111 ttm = localtime(&amendment->date);
112 snprintf(date, sizeof(date), "%04d-%02d-%02d %02d:%02d:%02d",
113 ttm->tm_year + 1900, ttm->tm_mon + 1, ttm->tm_mday,
114 ttm->tm_hour, ttm->tm_min, ttm->tm_sec);
115 TEInsert(date, strlen(date), editor->date_te);
116
117 /* log message */
118 bounds.top = bounds.bottom + PADDING;
119 fh = FontHeight(monaco, 9);
120 bounds.bottom = editor->win->portRect.bottom - 20 - (PADDING * 2);
121 bounds.right = editor->win->portRect.right - SCROLLBAR_WIDTH -
122 PADDING;
123 te_bounds = bounds;
124 InsetRect(&te_bounds, 2, 2);
125 TextFont(monaco);
126 TextSize(9);
127 editor->log_te = TEStylNew(&te_bounds, &bounds);
128 style.tsFont = monaco;
129 style.tsSize = 9;
130 TESetStyle(doFont | doSize, &style, false, editor->log_te);
131 TEAutoView(true, editor->log_te);
132 TETabEnable(editor->log_te);
133 HLock(amendment->log);
134 TEInsert(*(amendment->log), amendment->log_len, editor->log_te);
135 HUnlock(amendment->log);
136
137 /* scrollbar for log message */
138 bounds.left = bounds.right;
139 bounds.right += SCROLLBAR_WIDTH;
140 bounds.bottom++;
141 bounds.top--;
142 editor->log_scroller = NewControl(editor->win, &bounds, "\p",
143 true, 1, 1, 1, scrollBarProc, 0L);
144
145 /* save button */
146 bounds.left = editor->win->portRect.right - PADDING - 100;
147 bounds.right = bounds.left + 100;
148 bounds.bottom = editor->win->portRect.bottom - PADDING;
149 bounds.top = bounds.bottom - 20;
150 editor->save_button = NewControl(editor->win, &bounds, "\pSave",
151 true, 1, 1, 1, pushButProc, 0L);
152
153 editor->last_te = editor->author_te;
154
155 focusable = xmalloczero(sizeof(struct focusable), "editor focusable");
156 focusable->cookie = editor;
157 focusable->win = editor->win;
158 focusable->modal = true;
159 focusable->idle = editor_idle;
160 focusable->update = editor_update;
161 focusable->mouse_down = editor_mouse_down;
162 focusable->key_down = editor_key_down;
163 focusable->menu = editor_handle_menu;
164 focusable->close = editor_close;
165 focusable_add(focusable);
166 }
167
168 bool
169 editor_close(struct focusable *focusable)
170 {
171 struct editor *editor = (struct editor *)focusable->cookie;
172
173 TEDispose(editor->author_te);
174 TEDispose(editor->date_te);
175 TEDispose(editor->log_te);
176 DisposeWindow(editor->win);
177
178 xfree(&editor);
179
180 return true;
181 }
182
183 void
184 editor_idle(struct focusable *focusable, EventRecord *event)
185 {
186 struct editor *editor = (struct editor *)focusable->cookie;
187
188 TEIdle(editor->last_te);
189 }
190
191 void
192 editor_update(struct focusable *focusable, EventRecord *event)
193 {
194 Str255 buf;
195 Rect r;
196 short what = -1, len;
197 struct editor *editor = (struct editor *)focusable->cookie;
198
199 if (event != NULL)
200 what = event->what;
201
202 switch (what) {
203 case -1:
204 case updateEvt:
205 r = (*(editor->author_te))->viewRect;
206 MoveTo(PADDING, r.top + FontHeight(LABEL_FONT, LABEL_FONT_SIZE) - 2);
207 TextFont(LABEL_FONT);
208 TextSize(LABEL_FONT_SIZE);
209 DrawText("Author:", 0, 7);
210 InsetRect(&r, -1, -1);
211 FrameRect(&r);
212 TEUpdate(&r, editor->author_te);
213
214 r = (*(editor->date_te))->viewRect;
215 MoveTo(PADDING, r.top + FontHeight(LABEL_FONT, LABEL_FONT_SIZE) - 2);
216 TextFont(LABEL_FONT);
217 TextSize(LABEL_FONT_SIZE);
218 DrawText("Date:", 0, 5);
219 InsetRect(&r, -1, -1);
220 FrameRect(&r);
221 TEUpdate(&r, editor->date_te);
222
223 r = (*(editor->log_te))->viewRect;
224 MoveTo(PADDING, r.top + FontHeight(LABEL_FONT, LABEL_FONT_SIZE) - 2);
225 TextFont(LABEL_FONT);
226 TextSize(LABEL_FONT_SIZE);
227 DrawText("Log:", 0, 4);
228 InsetRect(&r, -1, -1);
229 FrameRect(&r);
230 TEUpdate(&r, editor->log_te);
231
232 editor_update_menu(editor);
233 UpdtControl(editor->win, editor->win->visRgn);
234
235 break;
236 }
237 }
238
239 void
240 editor_suspend(struct focusable *focusable)
241 {
242 struct editor *editor = (struct editor *)focusable->cookie;
243
244 TEDeactivate(editor->author_te);
245 TEDeactivate(editor->date_te);
246 TEDeactivate(editor->log_te);
247 }
248
249 void
250 editor_resume(struct focusable *focusable)
251 {
252 struct editor *editor = (struct editor *)focusable->cookie;
253
254 TEActivate(editor->author_te);
255 TEActivate(editor->date_te);
256 TEActivate(editor->log_te);
257 }
258
259 void
260 editor_key_down(struct focusable *focusable, EventRecord *event)
261 {
262 struct editor *editor = (struct editor *)focusable->cookie;
263 char k;
264
265 k = (event->message & charCodeMask);
266
267 if (k == '\r' && (editor->last_te == editor->author_te ||
268 editor->last_te == editor->date_te))
269 return;
270
271 TEKey(k, editor->last_te);
272 if (editor->last_te == editor->log_te)
273 UpdateScrollbarForTE(editor->win, editor->log_scroller,
274 editor->last_te, false);
275 editor_update_menu(editor);
276 }
277
278 void
279 editor_mouse_down(struct focusable *focusable, EventRecord *event)
280 {
281 struct editor *editor = (struct editor *)focusable->cookie;
282 Point p;
283 ControlHandle control;
284 Rect r;
285 short val, adj, page, was_selected, part, i;
286
287 p = event->where;
288 GlobalToLocal(&p);
289
290 r = (*(editor->author_te))->viewRect;
291 if (PtInRect(p, &r)) {
292 if (editor->last_te != editor->author_te) {
293 editor->last_te = editor->author_te;
294 TEDeactivate(editor->date_te);
295 TEDeactivate(editor->log_te);
296 TEActivate(editor->author_te);
297 }
298 TEClick(p, ((event->modifiers & shiftKey) != 0), editor->author_te);
299 editor_update_menu(editor);
300 return;
301 }
302
303
304 r = (*(editor->date_te))->viewRect;
305 if (PtInRect(p, &r)) {
306 if (editor->last_te != editor->date_te) {
307 editor->last_te = editor->date_te;
308 TEDeactivate(editor->author_te);
309 TEDeactivate(editor->log_te);
310 TEActivate(editor->date_te);
311 }
312 TEClick(p, ((event->modifiers & shiftKey) != 0), editor->date_te);
313 editor_update_menu(editor);
314 return;
315 }
316
317 r = (*(editor->log_te))->viewRect;
318 if (PtInRect(p, &r)) {
319 if (editor->last_te != editor->log_te) {
320 editor->last_te = editor->log_te;
321 TEDeactivate(editor->author_te);
322 TEDeactivate(editor->date_te);
323 TEActivate(editor->log_te);
324 }
325 TEClick(p, ((event->modifiers & shiftKey) != 0), editor->log_te);
326 editor_update_menu(editor);
327 return;
328 }
329
330 switch (part = FindControl(p, editor->win, &control)) {
331 case inButton:
332 if (TrackControl(control, p, 0L) &&
333 control == editor->save_button)
334 editor_save(editor);
335 break;
336 case inUpButton:
337 case inDownButton:
338 case inPageUp:
339 case inPageDown:
340 if (control == editor->log_scroller)
341 SetTrackControlTE(editor->log_te);
342 else
343 break;
344 TrackControl(control, p, TrackMouseDownInControl);
345 break;
346 case inThumb:
347 val = GetCtlValue(control);
348 if (TrackControl(control, p, 0L) == 0)
349 break;
350 adj = val - GetCtlValue(control);
351 if (adj != 0) {
352 val -= adj;
353 if (control == editor->log_scroller)
354 TEScroll(0, adj * TEGetHeight(0, 0, editor->log_te),
355 editor->log_te);
356 SetCtlValue(control, val);
357 }
358 break;
359 }
360 }
361
362 void
363 editor_update_menu(struct editor *editor)
364 {
365 if ((*(editor->last_te))->selStart == (*(editor->last_te))->selEnd) {
366 DisableItem(edit_menu, EDIT_MENU_CUT_ID);
367 DisableItem(edit_menu, EDIT_MENU_COPY_ID);
368 } else {
369 EnableItem(edit_menu, EDIT_MENU_CUT_ID);
370 EnableItem(edit_menu, EDIT_MENU_COPY_ID);
371 }
372 if ((*(editor->last_te))->nLines > 0)
373 EnableItem(edit_menu, EDIT_MENU_SELECT_ALL_ID);
374 else
375 DisableItem(edit_menu, EDIT_MENU_SELECT_ALL_ID);
376 EnableItem(edit_menu, EDIT_MENU_PASTE_ID);
377
378 DisableItem(repo_menu, REPO_MENU_ADD_FILE_ID);
379 DisableItem(repo_menu, REPO_MENU_DISCARD_CHANGES_ID);
380 DisableItem(repo_menu, REPO_MENU_APPLY_PATCH_ID);
381
382 DisableItem(amendment_menu, AMENDMENT_MENU_EDIT_ID);
383 DisableItem(amendment_menu, AMENDMENT_MENU_EXPORT_ID);
384
385 EnableItem(repo_menu, 0);
386 }
387
388 bool
389 editor_handle_menu(struct focusable *focusable, short menu, short item)
390 {
391 struct editor *editor = (struct editor *)focusable->cookie;
392
393 switch (menu) {
394 case EDIT_MENU_ID:
395 switch (item) {
396 case EDIT_MENU_CUT_ID:
397 TECut(editor->last_te);
398 editor_update_menu(editor);
399 return true;
400 case EDIT_MENU_COPY_ID:
401 TECopy(editor->last_te);
402 editor_update_menu(editor);
403 return true;
404 case EDIT_MENU_PASTE_ID:
405 TEPaste(editor->last_te);
406 editor_update_menu(editor);
407 return true;
408 case EDIT_MENU_SELECT_ALL_ID:
409 TESetSelect(0, 1024 * 32, editor->last_te);
410 editor_update_menu(editor);
411 return true;
412 }
413 break;
414 }
415
416 return false;
417 }
418
419 void
420 editor_save(struct editor *editor)
421 {
422 struct tm ttm;
423 size_t len, size;
424 time_t ts;
425 short ret, yy, mm, dd, hh, min, ss, count = 0;
426 char *date, *author, *log, *data;
427
428 if ((*(editor->author_te))->teLength == 0) {
429 warn("Author field cannot be blank");
430 return;
431 }
432
433 len = (*(editor->date_te))->teLength;
434 if (len == 0) {
435 warn("Date field cannot be blank");
436 return;
437 }
438
439 date = xmalloc(len + 1, "editor_save");
440 memcpy(date, *(*(editor->date_te))->hText, len);
441 date[len] = '\0';
442
443 ret = sscanf(date, "%d-%d-%d %d:%d:%d%n", &yy, &mm, &dd, &hh, &min,
444 &ss, &count);
445 xfree(&date);
446 if (ret != 6 || count < 11) {
447 warn("Date must be in YYYY-MM-DD HH:MM:SS format");
448 return;
449 }
450
451 ttm.tm_year = yy - 1900;
452 ttm.tm_mon = mm - 1;
453 ttm.tm_mday = dd;
454 ttm.tm_hour = hh;
455 ttm.tm_min = min;
456 ttm.tm_sec = ss;
457 ts = mktime(&ttm);
458
459 len = (*(editor->log_te))->teLength;
460 if (len == 0) {
461 warn("Log cannot be blank");
462 return;
463 }
464
465 editor->amendment->date = ts;
466
467 len = sizeof(editor->amendment->author) - 1;
468 if ((*(editor->author_te))->teLength < len)
469 len = (*(editor->author_te))->teLength;
470 memcpy(editor->amendment->author, *(*(editor->author_te))->hText,
471 len);
472 editor->amendment->author[len] = '\0';
473
474 editor->amendment->log_len = (*(editor->log_te))->teLength;
475 if (editor->amendment->log)
476 DisposHandle(editor->amendment->log);
477
478 editor->amendment->log = xNewHandle(editor->amendment->log_len);
479 memcpy(*(editor->amendment->log), *(*(editor->log_te))->hText,
480 editor->amendment->log_len);
481
482 progress("Storing updated amendment metadata...");
483
484 repo_marshall_amendment(editor->amendment, &data, &len);
485
486 size = bile_write(editor->browser->repo->bile, REPO_AMENDMENT_RTYPE,
487 editor->amendment->id, data, len);
488 if (size != len)
489 panic("Failed storing amendment in repo file: %d",
490 bile_error(editor->browser->repo->bile));
491 xfree(&data);
492
493 editor->browser->need_refresh = true;
494 focusable_close(focusable_find(editor->win));
495 progress(NULL);
496 }