AmendHub

Download

jcs

/

amend

/

editor.c

 

(View History)

jcs   editor: Update scrollbar for log window at launch Latest amendment: 252 on 2023-10-24

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 bounds.bottom = editor->win->portRect.bottom - 20 - (PADDING * 2);
120 bounds.right = editor->win->portRect.right - SCROLLBAR_WIDTH -
121 PADDING;
122 te_bounds = bounds;
123 InsetRect(&te_bounds, 2, 2);
124 TextFont(monaco);
125 TextSize(9);
126 editor->log_te = TEStylNew(&te_bounds, &bounds);
127 style.tsFont = monaco;
128 style.tsSize = 9;
129 TESetStyle(doFont | doSize, &style, false, editor->log_te);
130 TEAutoView(true, editor->log_te);
131 TETabEnable(editor->log_te);
132 HLock(amendment->log);
133 TEInsert(*(amendment->log), amendment->log_len, editor->log_te);
134 HUnlock(amendment->log);
135
136 /* scrollbar for log message */
137 bounds.left = bounds.right;
138 bounds.right += SCROLLBAR_WIDTH;
139 bounds.bottom++;
140 bounds.top--;
141 editor->log_scroller = NewControl(editor->win, &bounds, "\p",
142 true, 1, 1, 1, scrollBarProc, 0L);
143
144 /* save button */
145 bounds.left = editor->win->portRect.right - PADDING - 100;
146 bounds.right = bounds.left + 100;
147 bounds.bottom = editor->win->portRect.bottom - PADDING;
148 bounds.top = bounds.bottom - 20;
149 editor->save_button = NewControl(editor->win, &bounds, "\pSave",
150 true, 1, 1, 1, pushButProc, 0L);
151
152 UpdateScrollbarForTE(editor->win, editor->log_scroller,
153 editor->log_te, false);
154
155 editor->last_te = editor->author_te;
156
157 focusable = xmalloczero(sizeof(struct focusable), "editor focusable");
158 focusable->cookie = editor;
159 focusable->win = editor->win;
160 focusable->modal = true;
161 focusable->idle = editor_idle;
162 focusable->update = editor_update;
163 focusable->mouse_down = editor_mouse_down;
164 focusable->key_down = editor_key_down;
165 focusable->menu = editor_handle_menu;
166 focusable->close = editor_close;
167 focusable_add(focusable);
168 }
169
170 bool
171 editor_close(struct focusable *focusable)
172 {
173 struct editor *editor = (struct editor *)focusable->cookie;
174
175 TEDispose(editor->author_te);
176 TEDispose(editor->date_te);
177 TEDispose(editor->log_te);
178 DisposeWindow(editor->win);
179
180 xfree(&editor);
181
182 return true;
183 }
184
185 void
186 editor_idle(struct focusable *focusable, EventRecord *event)
187 {
188 struct editor *editor = (struct editor *)focusable->cookie;
189
190 TEIdle(editor->last_te);
191 }
192
193 void
194 editor_update(struct focusable *focusable, EventRecord *event)
195 {
196 Rect r;
197 short what = -1;
198 struct editor *editor = (struct editor *)focusable->cookie;
199
200 if (event != NULL)
201 what = event->what;
202
203 switch (what) {
204 case -1:
205 case updateEvt:
206 r = (*(editor->author_te))->viewRect;
207 MoveTo(PADDING, r.top + FontHeight(LABEL_FONT, LABEL_FONT_SIZE) - 2);
208 TextFont(LABEL_FONT);
209 TextSize(LABEL_FONT_SIZE);
210 DrawText("Author:", 0, 7);
211 InsetRect(&r, -1, -1);
212 FrameRect(&r);
213 TEUpdate(&r, editor->author_te);
214
215 r = (*(editor->date_te))->viewRect;
216 MoveTo(PADDING, r.top + FontHeight(LABEL_FONT, LABEL_FONT_SIZE) - 2);
217 TextFont(LABEL_FONT);
218 TextSize(LABEL_FONT_SIZE);
219 DrawText("Date:", 0, 5);
220 InsetRect(&r, -1, -1);
221 FrameRect(&r);
222 TEUpdate(&r, editor->date_te);
223
224 r = (*(editor->log_te))->viewRect;
225 MoveTo(PADDING, r.top + FontHeight(LABEL_FONT, LABEL_FONT_SIZE) - 2);
226 TextFont(LABEL_FONT);
227 TextSize(LABEL_FONT_SIZE);
228 DrawText("Log:", 0, 4);
229 InsetRect(&r, -1, -1);
230 FrameRect(&r);
231 TEUpdate(&r, editor->log_te);
232
233 editor_update_menu(editor);
234 UpdtControl(editor->win, editor->win->visRgn);
235
236 break;
237 }
238 }
239
240 void
241 editor_suspend(struct focusable *focusable)
242 {
243 struct editor *editor = (struct editor *)focusable->cookie;
244
245 TEDeactivate(editor->author_te);
246 TEDeactivate(editor->date_te);
247 TEDeactivate(editor->log_te);
248 }
249
250 void
251 editor_resume(struct focusable *focusable)
252 {
253 struct editor *editor = (struct editor *)focusable->cookie;
254
255 TEActivate(editor->author_te);
256 TEActivate(editor->date_te);
257 TEActivate(editor->log_te);
258 }
259
260 void
261 editor_key_down(struct focusable *focusable, EventRecord *event)
262 {
263 struct editor *editor = (struct editor *)focusable->cookie;
264 char k;
265
266 k = (event->message & charCodeMask);
267
268 if (k == '\r' && (editor->last_te == editor->author_te ||
269 editor->last_te == editor->date_te))
270 return;
271
272 TEKey(k, editor->last_te);
273 if (editor->last_te == editor->log_te)
274 UpdateScrollbarForTE(editor->win, editor->log_scroller,
275 editor->last_te, false);
276 editor_update_menu(editor);
277 }
278
279 void
280 editor_mouse_down(struct focusable *focusable, EventRecord *event)
281 {
282 struct editor *editor = (struct editor *)focusable->cookie;
283 Point p;
284 ControlHandle control;
285 Rect r;
286 short val, adj;
287
288 p = event->where;
289 GlobalToLocal(&p);
290
291 r = (*(editor->author_te))->viewRect;
292 if (PtInRect(p, &r)) {
293 if (editor->last_te != editor->author_te) {
294 editor->last_te = editor->author_te;
295 TEDeactivate(editor->date_te);
296 TEDeactivate(editor->log_te);
297 TEActivate(editor->author_te);
298 }
299 TEClick(p, ((event->modifiers & shiftKey) != 0), editor->author_te);
300 editor_update_menu(editor);
301 return;
302 }
303
304
305 r = (*(editor->date_te))->viewRect;
306 if (PtInRect(p, &r)) {
307 if (editor->last_te != editor->date_te) {
308 editor->last_te = editor->date_te;
309 TEDeactivate(editor->author_te);
310 TEDeactivate(editor->log_te);
311 TEActivate(editor->date_te);
312 }
313 TEClick(p, ((event->modifiers & shiftKey) != 0), editor->date_te);
314 editor_update_menu(editor);
315 return;
316 }
317
318 r = (*(editor->log_te))->viewRect;
319 if (PtInRect(p, &r)) {
320 if (editor->last_te != editor->log_te) {
321 editor->last_te = editor->log_te;
322 TEDeactivate(editor->author_te);
323 TEDeactivate(editor->date_te);
324 TEActivate(editor->log_te);
325 }
326 TEClick(p, ((event->modifiers & shiftKey) != 0), editor->log_te);
327 editor_update_menu(editor);
328 return;
329 }
330
331 switch (FindControl(p, editor->win, &control)) {
332 case inButton:
333 if (TrackControl(control, p, 0L) &&
334 control == editor->save_button)
335 editor_save(editor);
336 break;
337 case inUpButton:
338 case inDownButton:
339 case inPageUp:
340 case inPageDown:
341 if (control == editor->log_scroller)
342 SetTrackControlTE(editor->log_te);
343 else
344 break;
345 TrackControl(control, p, TrackMouseDownInControl);
346 break;
347 case inThumb:
348 val = GetCtlValue(control);
349 if (TrackControl(control, p, 0L) == 0)
350 break;
351 adj = val - GetCtlValue(control);
352 if (adj != 0) {
353 val -= adj;
354 if (control == editor->log_scroller)
355 TEScroll(0, adj * TEGetHeight(0, 0, editor->log_te),
356 editor->log_te);
357 SetCtlValue(control, val);
358 }
359 break;
360 }
361 }
362
363 void
364 editor_update_menu(struct editor *editor)
365 {
366 if ((*(editor->last_te))->selStart == (*(editor->last_te))->selEnd) {
367 DisableItem(edit_menu, EDIT_MENU_CUT_ID);
368 DisableItem(edit_menu, EDIT_MENU_COPY_ID);
369 } else {
370 EnableItem(edit_menu, EDIT_MENU_CUT_ID);
371 EnableItem(edit_menu, EDIT_MENU_COPY_ID);
372 }
373 if ((*(editor->last_te))->nLines > 0)
374 EnableItem(edit_menu, EDIT_MENU_SELECT_ALL_ID);
375 else
376 DisableItem(edit_menu, EDIT_MENU_SELECT_ALL_ID);
377 EnableItem(edit_menu, EDIT_MENU_PASTE_ID);
378
379 DisableItem(repo_menu, REPO_MENU_ADD_FILE_ID);
380 DisableItem(repo_menu, REPO_MENU_DISCARD_CHANGES_ID);
381 DisableItem(repo_menu, REPO_MENU_APPLY_DIFF_ID);
382
383 DisableItem(amendment_menu, AMENDMENT_MENU_EDIT_ID);
384 DisableItem(amendment_menu, AMENDMENT_MENU_EXPORT_ID);
385
386 EnableItem(repo_menu, 0);
387 }
388
389 bool
390 editor_handle_menu(struct focusable *focusable, short menu, short item)
391 {
392 struct editor *editor = (struct editor *)focusable->cookie;
393
394 switch (menu) {
395 case EDIT_MENU_ID:
396 switch (item) {
397 case EDIT_MENU_CUT_ID:
398 TECut(editor->last_te);
399 editor_update_menu(editor);
400 if (editor->last_te == editor->log_te)
401 UpdateScrollbarForTE(editor->win, editor->log_scroller,
402 editor->last_te, false);
403 return true;
404 case EDIT_MENU_COPY_ID:
405 TECopy(editor->last_te);
406 editor_update_menu(editor);
407 return true;
408 case EDIT_MENU_PASTE_ID:
409 TEPaste(editor->last_te);
410 if (editor->last_te == editor->log_te)
411 UpdateScrollbarForTE(editor->win, editor->log_scroller,
412 editor->last_te, false);
413 editor_update_menu(editor);
414 return true;
415 case EDIT_MENU_SELECT_ALL_ID:
416 TESetSelect(0, 1024 * 32, editor->last_te);
417 editor_update_menu(editor);
418 return true;
419 }
420 break;
421 }
422
423 return false;
424 }
425
426 void
427 editor_save(struct editor *editor)
428 {
429 struct tm ttm;
430 size_t len, size;
431 time_t ts;
432 short ret, yy, mm, dd, hh, min, ss, count = 0;
433 char *date, *data;
434
435 if ((*(editor->author_te))->teLength == 0) {
436 warn("Author field cannot be blank");
437 return;
438 }
439
440 len = (*(editor->date_te))->teLength;
441 if (len == 0) {
442 warn("Date field cannot be blank");
443 return;
444 }
445
446 date = xmalloc(len + 1, "editor_save");
447 memcpy(date, *(*(editor->date_te))->hText, len);
448 date[len] = '\0';
449
450 ret = sscanf(date, "%d-%d-%d %d:%d:%d%n", &yy, &mm, &dd, &hh, &min,
451 &ss, &count);
452 xfree(&date);
453 if (ret != 6 || count < 11) {
454 warn("Date must be in YYYY-MM-DD HH:MM:SS format");
455 return;
456 }
457
458 ttm.tm_year = yy - 1900;
459 ttm.tm_mon = mm - 1;
460 ttm.tm_mday = dd;
461 ttm.tm_hour = hh;
462 ttm.tm_min = min;
463 ttm.tm_sec = ss;
464 ts = mktime(&ttm);
465
466 len = (*(editor->log_te))->teLength;
467 if (len == 0) {
468 warn("Log cannot be blank");
469 return;
470 }
471
472 editor->amendment->date = ts;
473
474 len = sizeof(editor->amendment->author) - 1;
475 if ((*(editor->author_te))->teLength < len)
476 len = (*(editor->author_te))->teLength;
477 memcpy(editor->amendment->author, *(*(editor->author_te))->hText,
478 len);
479 editor->amendment->author[len] = '\0';
480
481 editor->amendment->log_len = (*(editor->log_te))->teLength;
482 if (editor->amendment->log)
483 DisposHandle(editor->amendment->log);
484
485 editor->amendment->log = xNewHandle(editor->amendment->log_len);
486 memcpy(*(editor->amendment->log), *(*(editor->log_te))->hText,
487 editor->amendment->log_len);
488
489 progress("Storing updated amendment metadata...");
490
491 repo_marshall_amendment(editor->amendment, &data, &len);
492
493 size = bile_write(editor->browser->repo->bile, REPO_AMENDMENT_RTYPE,
494 editor->amendment->id, data, len);
495 if (size != len)
496 panic("Failed storing amendment in repo file: %d",
497 bile_error(editor->browser->repo->bile));
498 xfree(&data);
499
500 editor->browser->need_refresh = true;
501 focusable_close(focusable_find(editor->win));
502 progress(NULL);
503 }