AmendHub

Download

jcs

/

amend

/

browser.c

 

(View History)

jcs   *: Add author e-mail to settings and amendments Latest amendment: 181 on 2026-10-01

1 /*
2 * Copyright (c) 2021-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 "amend.h"
22 #include "browser.h"
23 #include "committer.h"
24 #include "diff.h"
25 #include "editor.h"
26 #include "focusable.h"
27 #include "patch.h"
28 #include "repo.h"
29 #include "settings.h"
30 #include "util.h"
31
32 #define DIFF_BUTTON_FONT geneva
33 #define DIFF_BUTTON_FONT_SIZE 12
34
35 #define PADDING 10
36
37 bool browser_close(struct focusable *focusable);
38 void browser_idle(struct focusable *focusable, EventRecord *event);
39 void browser_update_menu(struct browser *browser);
40 void browser_update(struct focusable *focusable, EventRecord *event);
41 void browser_patch_ldefs(void);
42 void browser_show_amendment(struct browser *browser,
43 struct repo_amendment *amendment);
44 void browser_mouse_down(struct focusable *focusable, EventRecord *event);
45 bool browser_handle_menu(struct focusable *focusable, short menu,
46 short item);
47
48 void browser_add_files(struct browser *browser);
49 void browser_filter_amendments(struct browser *browser);
50 void browser_discard_changes(struct browser *browser);
51 void browser_edit_amendment(struct browser *browser);
52
53 Pattern fill_pattern;
54 Handle amendment_list_ldef_h = NULL;
55 Handle file_list_ldef_h = NULL;
56
57 void
58 browser_idle(struct focusable *focusable, EventRecord *event)
59 {
60 struct browser *browser = (struct browser *)focusable->cookie;
61
62 switch (browser->state) {
63 case BROWSER_STATE_IDLE:
64 if (browser->need_refresh) {
65 browser->need_refresh = false;
66 browser->state = BROWSER_STATE_UPDATE_AMENDMENT_LIST;
67 }
68 break;
69 case BROWSER_STATE_ADD_FILE:
70 if (repo_add_file(browser->repo) == NULL)
71 browser->state = BROWSER_STATE_IDLE;
72 else
73 browser->state = BROWSER_STATE_UPDATE_FILE_LIST;
74 break;
75 case BROWSER_STATE_UPDATE_FILE_LIST:
76 browser_add_files(browser);
77 browser->state = BROWSER_STATE_IDLE;
78 break;
79 case BROWSER_STATE_UPDATE_AMENDMENT_LIST:
80 browser_filter_amendments(browser);
81 browser->state = BROWSER_STATE_IDLE;
82 break;
83 case BROWSER_STATE_OPEN_COMMITTER:
84 committer_init(browser);
85 browser->state = BROWSER_STATE_WAITING_FOR_COMMITTER;
86 break;
87 case BROWSER_STATE_WAITING_FOR_COMMITTER:
88 break;
89 case BROWSER_STATE_CHECKOUT:
90 browser_discard_changes(browser);
91 browser->state = BROWSER_STATE_IDLE;
92 break;
93 case BROWSER_STATE_EXPORT_AMENDMENT:
94 browser_export_amendment(browser);
95 browser->state = BROWSER_STATE_IDLE;
96 break;
97 case BROWSER_STATE_APPLY_PATCH:
98 browser_apply_patch(browser);
99 browser->state = BROWSER_STATE_IDLE;
100 break;
101 case BROWSER_STATE_EDIT_AMENDMENT:
102 browser_edit_amendment(browser);
103 browser->state = BROWSER_STATE_IDLE;
104 break;
105 }
106 }
107
108 struct browser *
109 browser_init(struct repo *repo)
110 {
111 char title[128], filename[256], *justfilename;
112 struct browser *browser;
113 struct focusable *focusable;
114 Rect bounds = { 0 }, data_bounds = { 0 }, tv_bounds = { 0 };
115 Rect padding;
116 Point cell_size = { 0, 0 };
117 Cell cell = { 0 };
118 short width, height;
119
120 browser = xmalloczero(sizeof(struct browser));
121 browser->state = BROWSER_STATE_IDLE;
122 browser->repo = repo;
123
124 GetIndPattern(&fill_pattern, sysPatListID, 22);
125
126 /* main window */
127 width = screenBits.bounds.right - screenBits.bounds.left - PADDING;
128 if (width > 540)
129 width = 540;
130 height = screenBits.bounds.bottom - screenBits.bounds.top -
131 PADDING - (GetMBarHeight() * 2);
132 if (height > 350)
133 height = 350;
134 center_in_screen(width, height, true, &bounds);
135
136 memcpy(filename, browser->repo->bile->filename, sizeof(filename));
137 PtoCstr(filename);
138 justfilename = strrchr(filename, ':');
139 if (justfilename == NULL)
140 justfilename = (char *)&filename;
141 else
142 justfilename++;
143 snprintf(title, sizeof(title), "%s: %s", PROGRAM_NAME, justfilename);
144 CtoPstr(title);
145
146 browser->win = NewWindow(0L, &bounds, title, false, noGrowDocProc,
147 (WindowPtr)-1L, true, 0);
148 if (!browser->win)
149 panic("Can't create window");
150 SetPort(browser->win);
151
152 browser_patch_ldefs();
153
154 /* file list */
155 bounds.top = bounds.left = PADDING;
156 bounds.bottom = browser->win->portRect.bottom -
157 (browser->win->portRect.bottom * 0.6) - 2 - 20 - PADDING - PADDING;
158 bounds.right = 100;
159
160 cell_size.v = FontHeight(FILE_LIST_FONT, FILE_LIST_FONT_SIZE);
161 browser->file_list = LNew(&bounds, &data_bounds, cell_size,
162 FILE_LDEF_ID, browser->win, true, true, false, true);
163 if (!browser->file_list)
164 panic("Can't create file list");
165 LAddColumn(1, 0, browser->file_list);
166 (*(browser->file_list))->selFlags = lNoNilHilite;
167
168 /* diff button */
169 bounds.top = bounds.bottom + PADDING;
170 bounds.bottom = bounds.top + 20;
171 bounds.right += SCROLLBAR_WIDTH;
172 TextFont(DIFF_BUTTON_FONT);
173 TextFace(0);
174 TextSize(DIFF_BUTTON_FONT_SIZE);
175 browser->diff_button = NewControl(browser->win, &bounds,
176 "\pGenerate Diff", true, 1, 1, 1, pushButProc | useWFont, 0L);
177
178 /* amendment list */
179 bounds.top = bounds.left = PADDING;
180 bounds.left = bounds.right + PADDING;
181 bounds.right = browser->win->portRect.right - SCROLLBAR_WIDTH - PADDING;
182
183 cell_size.v = (FontHeight(AMENDMENT_LIST_FONT,
184 AMENDMENT_LIST_FONT_SIZE) * 2) + 2;
185 browser->amendment_list = LNew(&bounds, &data_bounds, cell_size,
186 AMENDMENT_LDEF_ID, browser->win, true, true, false, true);
187 if (!browser->amendment_list)
188 panic("Can't create amendment list");
189 LAddColumn(1, 0, browser->amendment_list);
190 (*(browser->amendment_list))->selFlags = lOnlyOne;
191
192 /* diff text */
193 bounds.top = (*browser->amendment_list)->rView.bottom + PADDING;
194 bounds.left = PADDING;
195 bounds.right = browser->win->portRect.right - SCROLLBAR_WIDTH - PADDING;
196 bounds.bottom = browser->win->portRect.bottom - PADDING;
197 tv_bounds = bounds;
198 SetRect(&padding, 2, 2, 2, 2);
199 browser->diff_tv = TVNew(&bounds, &padding);
200 TVLineWrap(browser->diff_tv, false);
201 TextFont(DIFF_FONT);
202 TextFace(0);
203 TextSize(DIFF_FONT_SIZE);
204 TVTabStop(browser->diff_tv, (short)settings.tabstop * CharWidth(' '));
205 EraseRect(&bounds);
206
207 /* scrollbar for diff text */
208 bounds.right = browser->win->portRect.right - PADDING;
209 bounds.left = bounds.right - SCROLLBAR_WIDTH;
210 bounds.bottom++;
211 bounds.top--;
212 browser->diff_scroller = NewControl(browser->win, &bounds, "\p", true,
213 1, 1, 1, scrollBarProc, 0L);
214
215 browser_update_menu(browser);
216 browser_add_files(browser);
217 TVSetScrollbar(browser->diff_tv, browser->diff_scroller);
218 TVUpdateScrollbar(browser->diff_tv, browser->diff_scroller);
219
220 focusable = xmalloczero(sizeof(struct focusable));
221 focusable->cookie = browser;
222 focusable->win = browser->win;
223 focusable->idle = browser_idle;
224 focusable->update = browser_update;
225 focusable->mouse_down = browser_mouse_down;
226 focusable->menu = browser_handle_menu;
227 focusable->close = browser_close;
228 focusable_add(focusable);
229
230 progress(NULL);
231
232 SetCursor(&arrow);
233
234 return browser;
235 }
236
237 bool
238 browser_close(struct focusable *focusable)
239 {
240 struct browser *browser = (struct browser *)focusable->cookie;
241
242 if (browser->committer)
243 browser_close_committer(browser);
244
245 if (browser->repo)
246 repo_close(browser->repo);
247
248 TVDispose(browser->diff_tv);
249 LDispose(browser->file_list);
250 LDispose(browser->amendment_list);
251 DisposeWindow(browser->win);
252
253 xfree(&browser);
254 xfree(&focusable);
255
256 /* TODO: only do this if there aren't other browsers to ask */
257 menu_defaults();
258
259 return true;
260 }
261
262 void
263 browser_close_committer(struct browser *browser)
264 {
265 struct focusable *f;
266
267 if (browser->committer) {
268 f = focusable_find(browser->committer->win);
269 if (f)
270 focusable_close(f);
271 }
272 }
273
274 void
275 browser_add_files(struct browser *browser)
276 {
277 Cell cell = { 0, 0 };
278 short i;
279
280 LDoDraw(false, browser->file_list);
281 LDelRow(0, 0, browser->file_list);
282 browser_show_amendment(browser, NULL);
283
284 LAddRow(1, cell.v, browser->file_list);
285 /* will become "[All Files]" */
286 LSetCell(NULL, sizeof(Ptr), cell, browser->file_list);
287 LSetSelect(true, cell, browser->file_list);
288 cell.v++;
289
290 /* fill in files */
291 for (i = 0; i < browser->repo->nfiles; i++) {
292 LAddRow(1, cell.v, browser->file_list);
293 LSetCell(&browser->repo->files[i], sizeof(Ptr), cell,
294 browser->file_list);
295 cell.v++;
296 }
297
298 LDoDraw(true, browser->file_list);
299
300 browser_filter_amendments(browser);
301 InvalRect(&browser->win->portRect);
302 }
303
304 short
305 browser_is_all_files_selected(struct browser *browser)
306 {
307 Cell cell = { 0 };
308
309 if (browser->repo->nfiles == 0)
310 return 0;
311
312 /*
313 * "All Files" is always cell.v=0, so if v=0 is selected or nothing
314 * is, select all files
315 */
316 if (LGetSelect(false, &cell, browser->file_list) == true ||
317 LGetSelect(true, &cell, browser->file_list) == false)
318 return 1;
319
320 return 0;
321 }
322
323 short
324 browser_selected_file_ids(struct browser *browser, short **selected_files)
325 {
326 Cell cell = { 0 };
327 short nselected_files = 0, i;
328
329 if (browser->repo->nfiles == 0) {
330 *selected_files = NULL;
331 return 0;
332 }
333
334 *selected_files = xcalloc(browser->repo->nfiles, sizeof(short));
335
336 if (browser_is_all_files_selected(browser)) {
337 nselected_files = browser->repo->nfiles;
338 for (i = 0; i < browser->repo->nfiles; i++)
339 (*selected_files)[i] = browser->repo->files[i]->id;
340 } else {
341 cell.v = 0;
342 while (LGetSelect(true, &cell, browser->file_list)) {
343 (*selected_files)[nselected_files] =
344 browser->repo->files[cell.v - 1]->id;
345 nselected_files++;
346 LNextCell(true, true, &cell, browser->file_list);
347 }
348 }
349
350 return nselected_files;
351 }
352
353 void
354 browser_filter_amendments(struct browser *browser)
355 {
356 Cell cell = { 0 };
357 struct repo_amendment *amendment;
358 short i, j, k, add = 0;
359 short *selected_files = NULL;
360 short nselected_files = 0;
361
362 LDoDraw(false, browser->amendment_list);
363 LDelRow(0, 0, browser->amendment_list);
364 browser_show_amendment(browser, NULL);
365
366 /* fill in amendments for selected files */
367 nselected_files = browser_selected_file_ids(browser, &selected_files);
368 cell.v = 0;
369 for (i = browser->repo->namendments - 1; i >= 0; i--) {
370 add = 0;
371 amendment = browser->repo->amendments[i];
372 if (amendment == NULL)
373 continue;
374 for (j = 0; j < amendment->nfiles; j++) {
375 for (k = 0; k < nselected_files; k++) {
376 if (selected_files[k] == amendment->file_ids[j]) {
377 add = 1;
378 break;
379 }
380 }
381 if (add)
382 break;
383 }
384
385 if (!add)
386 continue;
387
388 LAddRow(1, cell.v, browser->amendment_list);
389 LSetCell(&(browser->repo->amendments[i]), sizeof(Ptr), cell,
390 browser->amendment_list);
391 cell.v++;
392 }
393
394 if (browser->repo->unloaded_amendments) {
395 LAddRow(1, cell.v, browser->amendment_list);
396 LSetCell(NULL, 0, cell, browser->amendment_list);
397 }
398
399 LDoDraw(true, browser->amendment_list);
400 InvalRect(&(*(browser->amendment_list))->rView);
401
402 if (selected_files)
403 xfree(&selected_files);
404 }
405
406 void
407 browser_show_amendment(struct browser *browser,
408 struct repo_amendment *amendment)
409 {
410 TVClear(browser->diff_tv);
411 TVUpdateScrollbar(browser->diff_tv, browser->diff_scroller);
412
413 if (amendment != NULL) {
414 SetCursor(*(GetCursor(watchCursor)));
415 repo_show_diff_text(browser->repo, amendment, browser->diff_tv);
416 SetCursor(&arrow);
417 }
418
419 TVUpdateScrollbar(browser->diff_tv, browser->diff_scroller);
420 browser_update_menu(browser);
421 }
422
423 void
424 browser_discard_changes(struct browser *browser)
425 {
426 char buf[256], filename[256];
427 struct repo_file *file;
428 short *selected = NULL;
429 short nselected = 0, i, error;
430 SFReply reply;
431
432 nselected = browser_selected_file_ids(browser, &selected);
433 for (i = 0; i < nselected; i++) {
434 file = repo_file_with_id(browser->repo, selected[i]);
435
436 snprintf(buf, sizeof(buf), "Save %s:", file->filename);
437 CtoPstr(buf);
438
439 strlcpy(filename, file->filename, sizeof(filename));
440 CtoPstr(filename);
441
442 SFPutFile(centered_sfput_dialog(), buf, filename, NULL, &reply);
443 if (!reply.good)
444 break;
445
446 error = repo_checkout_file(browser->repo, file, reply.vRefNum,
447 reply.fName);
448 if (error)
449 break;
450 }
451 }
452
453 void
454 browser_export_amendment(struct browser *browser)
455 {
456 Cell selected = { 0 };
457 struct repo_amendment *amendment;
458 short len;
459 SFReply reply;
460 char filename[255];
461
462 if (LGetSelect(true, &selected, browser->amendment_list) == false)
463 return;
464
465 len = sizeof(Ptr);
466 LGetCell(&amendment, &len, selected, browser->amendment_list);
467
468 snprintf(filename, sizeof(filename), "amendment_%d.diff",
469 amendment->id);
470 CtoPstr(filename);
471
472 SFPutFile(centered_sfput_dialog(), "\pSave as:", filename, NULL,
473 &reply);
474 if (!reply.good)
475 return;
476
477 repo_export_amendment(browser->repo, amendment, reply.vRefNum,
478 reply.fName);
479 }
480
481 void
482 browser_apply_patch(struct browser *browser)
483 {
484 SFReply reply;
485
486 SFGetFile(centered_sfget_dialog(), NULL, NULL, -1, NULL, NULL, &reply);
487 if (!reply.good)
488 return;
489
490 patch_process(browser->repo, reply.fName, reply.vRefNum);
491 }
492
493 void
494 browser_edit_amendment(struct browser *browser)
495 {
496 Cell selected = { 0 };
497 struct repo_amendment *amendment;
498 short len;
499
500 if (LGetSelect(true, &selected, browser->amendment_list) == false)
501 return;
502
503 len = sizeof(Ptr);
504 LGetCell(&amendment, &len, selected, browser->amendment_list);
505
506 editor_init(browser, amendment);
507 }
508
509 void
510 browser_patch_ldefs(void)
511 {
512 /* dynamically patch LDEFs to point to our custom functions */
513 if (file_list_ldef_h == NULL || *file_list_ldef_h == NULL ||
514 ((tCodeStub *)*file_list_ldef_h)->addr == 0) {
515 file_list_ldef_h = xGetResource('LDEF', FILE_LDEF_ID);
516 HLock(file_list_ldef_h);
517 ((tCodeStub *)*file_list_ldef_h)->addr = &file_list_ldef;
518 }
519
520 if (amendment_list_ldef_h == NULL || *amendment_list_ldef_h == NULL ||
521 ((tCodeStub *)*amendment_list_ldef_h)->addr == 0) {
522 amendment_list_ldef_h = xGetResource('LDEF', AMENDMENT_LDEF_ID);
523 HLock(amendment_list_ldef_h);
524 ((tCodeStub *)*amendment_list_ldef_h)->addr = &amendment_list_ldef;
525 }
526 }
527
528 void
529 browser_update_menu(struct browser *browser)
530 {
531 Cell cell = { 0, 0 };
532
533 TextFont(systemFont);
534 TextFace(0);
535 TextSize(12);
536
537 if (browser->repo->nfiles == 0) {
538 HiliteControl(browser->diff_button, 255);
539 DisableItem(file_menu, FILE_MENU_DIFF_ID);
540 DisableItem(file_menu, FILE_MENU_CHECKOUT_ID);
541 } else {
542 HiliteControl(browser->diff_button, 0);
543 EnableItem(file_menu, FILE_MENU_DIFF_ID);
544 EnableItem(file_menu, FILE_MENU_CHECKOUT_ID);
545 }
546
547 DisableItem(edit_menu, EDIT_MENU_CUT_ID);
548
549 if ((*(browser->diff_tv))->sel_start == (*(browser->diff_tv))->sel_end)
550 DisableItem(edit_menu, EDIT_MENU_COPY_ID);
551 else
552 EnableItem(edit_menu, EDIT_MENU_COPY_ID);
553
554 DisableItem(edit_menu, EDIT_MENU_PASTE_ID);
555
556 if ((*(browser->diff_tv))->nlines == 0)
557 DisableItem(edit_menu, EDIT_MENU_SELECT_ALL_ID);
558 else
559 EnableItem(edit_menu, EDIT_MENU_SELECT_ALL_ID);
560
561 EnableItem(repo_menu, REPO_MENU_ADD_FILE_ID);
562
563 if (LGetSelect(true, &cell, browser->amendment_list)) {
564 EnableItem(amendment_menu, AMENDMENT_MENU_EDIT_ID);
565 EnableItem(amendment_menu, AMENDMENT_MENU_EXPORT_ID);
566 } else {
567 DisableItem(amendment_menu, AMENDMENT_MENU_EDIT_ID);
568 DisableItem(amendment_menu, AMENDMENT_MENU_EXPORT_ID);
569 }
570 }
571
572 void
573 browser_update(struct focusable *focusable, EventRecord *event)
574 {
575 struct browser *browser = (struct browser *)focusable->cookie;
576 Rect r;
577 short what = -1;
578 Handle t;
579
580 if (event != NULL)
581 what = event->what;
582
583 switch (what) {
584 case -1:
585 case updateEvt:
586 FillRect(&browser->win->portRect, fill_pattern);
587
588 HLock(browser->diff_tv);
589 r = (*(browser->diff_tv))->view;
590 HUnlock(browser->diff_tv);
591 EraseRect(&r);
592 TVUpdate(browser->diff_tv);
593 InsetRect(&r, -1, -1);
594 FrameRect(&r);
595
596 /*
597 * Under heavy memory pressure, List Manager seems to close our
598 * custom resources and then re-open them, clearing the custom
599 * addrs we set during setup. Make sure they're still alive before
600 * doing an update or we'll jump to NULL.
601 */
602 browser_patch_ldefs();
603
604 r = (*(browser->file_list))->rView;
605 InsetRect(&r, -1, -1);
606 FillRect(&r, white);
607 FrameRect(&r);
608 LUpdate(browser->win->visRgn, browser->file_list);
609
610 r = (*(browser->amendment_list))->rView;
611 InsetRect(&r, -1, -1);
612 FillRect(&r, white);
613 FrameRect(&r);
614 LUpdate(browser->win->visRgn, browser->amendment_list);
615
616 TextFont(DIFF_BUTTON_FONT);
617 TextFace(0);
618 TextSize(DIFF_BUTTON_FONT_SIZE);
619 UpdtControl(browser->win, browser->win->visRgn);
620
621 browser_update_menu(browser);
622
623 break;
624 }
625 }
626
627 void
628 browser_mouse_down(struct focusable *focusable, EventRecord *event)
629 {
630 Cell selected = { 0 }, now = { 0 };
631 Point p;
632 ControlHandle control;
633 Rect r;
634 struct browser *browser = (struct browser *)focusable->cookie;
635 struct repo_amendment *amendment = NULL;
636 short *selected_files = NULL, *now_selected_files = NULL;
637 short nselected = 0, nnow_selected = 0;
638 short val, adj, was_selected, i, data_len;
639
640 p = event->where;
641 GlobalToLocal(&p);
642
643 /* is it in diff text? */
644 HLock(browser->diff_tv);
645 r = (*(browser->diff_tv))->view;
646 HUnlock(browser->diff_tv);
647 if (PtInRect(p, &r)) {
648 TVClick(browser->diff_tv, p, ((event->modifiers & shiftKey) != 0));
649 browser_update_menu(browser);
650 return;
651 }
652
653 /* is it in file list? */
654 r = (*(browser->file_list))->rView;
655 r.right += SCROLLBAR_WIDTH;
656 if (PtInRect(p, &r)) {
657 /* store what is selected now */
658 nselected = browser_selected_file_ids(browser, &selected_files);
659
660 /* possibly highlight a new cell */
661 LClick(p, event->modifiers, browser->file_list);
662
663 nnow_selected = browser_selected_file_ids(browser,
664 &now_selected_files);
665
666 if (nnow_selected == 0) {
667 /* can't select nothing, select 'all files' */
668 selected.v = 0;
669 LSetSelect(true, selected, browser->file_list);
670 if (nselected != browser->repo->nfiles)
671 browser_filter_amendments(browser);
672 } else if (nselected != nnow_selected) {
673 browser_filter_amendments(browser);
674 } else {
675 for (i = 0; i < nselected; i++) {
676 if (selected_files[i] != now_selected_files[i]) {
677 browser_filter_amendments(browser);
678 break;
679 }
680 }
681 }
682
683 if (selected_files)
684 xfree(&selected_files);
685 if (now_selected_files)
686 xfree(&now_selected_files);
687
688 return;
689 }
690
691 /* is it in amendment list? */
692 r = (*(browser->amendment_list))->rView;
693 r.right += SCROLLBAR_WIDTH;
694 if (PtInRect(p, &r)) {
695 if (browser->repo == NULL)
696 return;
697
698 /* store what is selected now */
699 was_selected = LGetSelect(true, &selected, browser->amendment_list);
700
701 /* possibly highlight a new cell */
702 LClick(p, event->modifiers, browser->amendment_list);
703
704 if (LGetSelect(true, &now, browser->amendment_list) == false) {
705 if (was_selected)
706 browser_show_amendment(browser, NULL);
707 } else if (!was_selected || selected.v != now.v) {
708 if (was_selected)
709 LSetSelect(false, selected, browser->amendment_list);
710 LSetSelect(true, now, browser->amendment_list);
711
712 /* in a filtered list, amendments[now.v] won't be accurate */
713 data_len = sizeof(Ptr);
714 LGetCell(&amendment, &data_len, now, browser->amendment_list);
715 if (amendment == NULL && browser->repo->unloaded_amendments) {
716 repo_load_amendments(browser->repo, true);
717 browser_filter_amendments(browser);
718 progress(NULL);
719 } else
720 browser_show_amendment(browser, amendment);
721 }
722
723 return;
724 }
725
726 switch (FindControl(p, browser->win, &control)) {
727 case inButton:
728 TextFont(DIFF_BUTTON_FONT);
729 TextFace(0);
730 TextSize(DIFF_BUTTON_FONT_SIZE);
731 if (TrackControl(control, p, 0L) &&
732 control == browser->diff_button)
733 browser->state = BROWSER_STATE_OPEN_COMMITTER;
734 break;
735 case inUpButton:
736 case inDownButton:
737 case inPageUp:
738 case inPageDown:
739 if (control == browser->diff_scroller)
740 TVSetTrackScrollControl(browser->diff_tv);
741 else
742 break;
743 TrackControl(control, p, TVTrackScrollControl);
744 break;
745 case inThumb:
746 val = GetCtlValue(control);
747 if (TrackControl(control, p, 0L) == 0)
748 break;
749 adj = val - GetCtlValue(control);
750 if (adj != 0) {
751 val -= adj;
752 if (control == browser->diff_scroller)
753 TVScroll(browser->diff_tv, 0, adj);
754 SetCtlValue(control, val);
755 }
756 break;
757 }
758 }
759
760 bool
761 browser_handle_menu(struct focusable *focusable, short menu, short item)
762 {
763 struct browser *browser = (struct browser *)focusable->cookie;
764
765 switch (menu) {
766 case FILE_MENU_ID:
767 switch (item) {
768 case FILE_MENU_DIFF_ID:
769 browser->state = BROWSER_STATE_OPEN_COMMITTER;
770 return true;
771 case FILE_MENU_CHECKOUT_ID:
772 browser->state = BROWSER_STATE_CHECKOUT;
773 return true;
774 }
775 break;
776 case EDIT_MENU_ID:
777 switch (item) {
778 case EDIT_MENU_COPY_ID:
779 TVCopy(browser->diff_tv);
780 return true;
781 case EDIT_MENU_SELECT_ALL_ID:
782 TVSetSelect(browser->diff_tv, 0, LONG_MAX);
783 return true;
784 }
785 break;
786 case REPO_MENU_ID:
787 switch (item) {
788 case REPO_MENU_ADD_FILE_ID:
789 browser->state = BROWSER_STATE_ADD_FILE;
790 return true;
791 case REPO_MENU_APPLY_PATCH_ID:
792 browser->state = BROWSER_STATE_APPLY_PATCH;
793 return true;
794 }
795 break;
796 case AMENDMENT_MENU_ID:
797 switch (item) {
798 case AMENDMENT_MENU_EDIT_ID:
799 browser->state = BROWSER_STATE_EDIT_AMENDMENT;
800 return true;
801 case AMENDMENT_MENU_EXPORT_ID:
802 browser->state = BROWSER_STATE_EXPORT_AMENDMENT;
803 return true;
804 }
805 break;
806 }
807
808 return false;
809 }