AmendHub

Download

jcs

/

amend

/

browser.c

 

(View History)

jcs   browser+committer: Set TextView tabstop to calculated width Latest amendment: 157 on 2025-12-17

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_DISCARD_CHANGES:
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_DIFF:
98 browser_apply_diff(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 TVUpdateScrollbar(browser->diff_tv, browser->diff_scroller);
218
219 focusable = xmalloczero(sizeof(struct focusable));
220 focusable->cookie = browser;
221 focusable->win = browser->win;
222 focusable->idle = browser_idle;
223 focusable->update = browser_update;
224 focusable->mouse_down = browser_mouse_down;
225 focusable->menu = browser_handle_menu;
226 focusable->close = browser_close;
227 focusable_add(focusable);
228
229 progress(NULL);
230
231 SetCursor(&arrow);
232
233 return browser;
234 }
235
236 bool
237 browser_close(struct focusable *focusable)
238 {
239 struct browser *browser = (struct browser *)focusable->cookie;
240
241 if (browser->committer)
242 browser_close_committer(browser);
243
244 if (browser->repo)
245 repo_close(browser->repo);
246
247 TVDispose(browser->diff_tv);
248 DisposeWindow(browser->win);
249
250 xfree(&browser);
251
252 menu_defaults();
253
254 return true;
255 }
256
257 void
258 browser_close_committer(struct browser *browser)
259 {
260 struct focusable *f;
261
262 if (browser->committer) {
263 f = focusable_find(browser->committer->win);
264 if (f)
265 focusable_close(f);
266 }
267 }
268
269 void
270 browser_add_files(struct browser *browser)
271 {
272 Cell cell = { 0, 0 };
273 short i;
274
275 LDoDraw(false, browser->file_list);
276 LDelRow(0, 0, browser->file_list);
277 browser_show_amendment(browser, NULL);
278
279 LAddRow(1, cell.v, browser->file_list);
280 /* will become "[All Files]" */
281 LSetCell(NULL, sizeof(Ptr), cell, browser->file_list);
282 LSetSelect(true, cell, browser->file_list);
283 cell.v++;
284
285 /* fill in files */
286 for (i = 0; i < browser->repo->nfiles; i++) {
287 LAddRow(1, cell.v, browser->file_list);
288 LSetCell(&browser->repo->files[i], sizeof(Ptr), cell,
289 browser->file_list);
290 cell.v++;
291 }
292
293 LDoDraw(true, browser->file_list);
294
295 browser_filter_amendments(browser);
296 InvalRect(&browser->win->portRect);
297 }
298
299 short
300 browser_is_all_files_selected(struct browser *browser)
301 {
302 Cell cell = { 0 };
303
304 if (browser->repo->nfiles == 0)
305 return 0;
306
307 /*
308 * "All Files" is always cell.v=0, so if v=0 is selected or nothing
309 * is, select all files
310 */
311 if (LGetSelect(false, &cell, browser->file_list) == true ||
312 LGetSelect(true, &cell, browser->file_list) == false)
313 return 1;
314
315 return 0;
316 }
317
318 short
319 browser_selected_file_ids(struct browser *browser, short **selected_files)
320 {
321 Cell cell = { 0 };
322 short nselected_files = 0, i;
323
324 if (browser->repo->nfiles == 0) {
325 *selected_files = NULL;
326 return 0;
327 }
328
329 *selected_files = xcalloc(browser->repo->nfiles, sizeof(short));
330
331 if (browser_is_all_files_selected(browser)) {
332 nselected_files = browser->repo->nfiles;
333 for (i = 0; i < browser->repo->nfiles; i++)
334 (*selected_files)[i] = browser->repo->files[i]->id;
335 } else {
336 cell.v = 0;
337 while (LGetSelect(true, &cell, browser->file_list)) {
338 (*selected_files)[nselected_files] =
339 browser->repo->files[cell.v - 1]->id;
340 nselected_files++;
341 LNextCell(true, true, &cell, browser->file_list);
342 }
343 }
344
345 return nselected_files;
346 }
347
348 void
349 browser_filter_amendments(struct browser *browser)
350 {
351 Cell cell = { 0 };
352 struct repo_amendment *amendment;
353 short i, j, k, add = 0;
354 short *selected_files = NULL;
355 short nselected_files = 0;
356
357 LDoDraw(false, browser->amendment_list);
358 LDelRow(0, 0, browser->amendment_list);
359 browser_show_amendment(browser, NULL);
360
361 /* fill in amendments for selected files */
362 nselected_files = browser_selected_file_ids(browser, &selected_files);
363 cell.v = 0;
364 for (i = browser->repo->namendments - 1; i >= 0; i--) {
365 add = 0;
366 amendment = browser->repo->amendments[i];
367 if (amendment == NULL)
368 continue;
369 for (j = 0; j < amendment->nfiles; j++) {
370 for (k = 0; k < nselected_files; k++) {
371 if (selected_files[k] == amendment->file_ids[j]) {
372 add = 1;
373 break;
374 }
375 }
376 if (add)
377 break;
378 }
379
380 if (!add)
381 continue;
382
383 LAddRow(1, cell.v, browser->amendment_list);
384 LSetCell(&(browser->repo->amendments[i]), sizeof(Ptr), cell,
385 browser->amendment_list);
386 cell.v++;
387 }
388
389 if (browser->repo->unloaded_amendments) {
390 LAddRow(1, cell.v, browser->amendment_list);
391 LSetCell(NULL, 0, cell, browser->amendment_list);
392 }
393
394 LDoDraw(true, browser->amendment_list);
395 InvalRect(&(*(browser->amendment_list))->rView);
396
397 if (selected_files)
398 xfree(&selected_files);
399 }
400
401 void
402 browser_show_amendment(struct browser *browser,
403 struct repo_amendment *amendment)
404 {
405 TVClear(browser->diff_tv);
406 TVUpdateScrollbar(browser->diff_tv, browser->diff_scroller);
407
408 if (amendment != NULL) {
409 SetCursor(*(GetCursor(watchCursor)));
410 repo_show_diff_text(browser->repo, amendment, browser->diff_tv);
411 SetCursor(&arrow);
412 }
413
414 TVUpdateScrollbar(browser->diff_tv, browser->diff_scroller);
415 browser_update_menu(browser);
416 }
417
418 void
419 browser_discard_changes(struct browser *browser)
420 {
421 char buf[256], filename[256];
422 struct repo_file *file;
423 short *selected = NULL;
424 short nselected = 0, i, error;
425 SFReply reply;
426
427 nselected = browser_selected_file_ids(browser, &selected);
428 for (i = 0; i < nselected; i++) {
429 file = repo_file_with_id(browser->repo, selected[i]);
430
431 snprintf(buf, sizeof(buf), "Save %s:", file->filename);
432 CtoPstr(buf);
433
434 strlcpy(filename, file->filename, sizeof(filename));
435 CtoPstr(filename);
436
437 SFPutFile(centered_sfput_dialog(), buf, filename, NULL, &reply);
438 if (!reply.good)
439 break;
440
441 error = repo_checkout_file(browser->repo, file, reply.vRefNum,
442 reply.fName);
443 if (error)
444 break;
445 }
446 }
447
448 void
449 browser_export_amendment(struct browser *browser)
450 {
451 Cell selected = { 0 };
452 struct repo_amendment *amendment;
453 short len;
454 SFReply reply;
455 char filename[255];
456
457 if (LGetSelect(true, &selected, browser->amendment_list) == false)
458 return;
459
460 len = sizeof(Ptr);
461 LGetCell(&amendment, &len, selected, browser->amendment_list);
462
463 snprintf(filename, sizeof(filename), "amendment_%d.diff",
464 amendment->id);
465 CtoPstr(filename);
466
467 SFPutFile(centered_sfput_dialog(), "\pSave as:", filename, NULL,
468 &reply);
469 if (!reply.good)
470 return;
471
472 repo_export_amendment(browser->repo, amendment, reply.vRefNum,
473 reply.fName);
474 }
475
476 void
477 browser_apply_diff(struct browser *browser)
478 {
479 SFReply reply;
480
481 SFGetFile(centered_sfget_dialog(), NULL, NULL, -1, NULL, NULL, &reply);
482 if (!reply.good)
483 return;
484
485 patch_process(browser->repo, reply.fName, reply.vRefNum);
486 }
487
488 void
489 browser_edit_amendment(struct browser *browser)
490 {
491 Cell selected = { 0 };
492 struct repo_amendment *amendment;
493 short len;
494
495 if (LGetSelect(true, &selected, browser->amendment_list) == false)
496 return;
497
498 len = sizeof(Ptr);
499 LGetCell(&amendment, &len, selected, browser->amendment_list);
500
501 editor_init(browser, amendment);
502 }
503
504 void
505 browser_patch_ldefs(void)
506 {
507 /* dynamically patch LDEFs to point to our custom functions */
508 if (file_list_ldef_h == NULL || *file_list_ldef_h == NULL ||
509 ((tCodeStub *)*file_list_ldef_h)->addr == 0) {
510 file_list_ldef_h = xGetResource('LDEF', FILE_LDEF_ID);
511 HLock(file_list_ldef_h);
512 ((tCodeStub *)*file_list_ldef_h)->addr = &file_list_ldef;
513 }
514
515 if (amendment_list_ldef_h == NULL || *amendment_list_ldef_h == NULL ||
516 ((tCodeStub *)*amendment_list_ldef_h)->addr == 0) {
517 amendment_list_ldef_h = xGetResource('LDEF', AMENDMENT_LDEF_ID);
518 HLock(amendment_list_ldef_h);
519 ((tCodeStub *)*amendment_list_ldef_h)->addr = &amendment_list_ldef;
520 }
521 }
522
523 void
524 browser_update_menu(struct browser *browser)
525 {
526 Cell cell = { 0, 0 };
527
528 TextFont(systemFont);
529 TextFace(0);
530 TextSize(12);
531
532 DisableItem(edit_menu, EDIT_MENU_CUT_ID);
533
534 if ((*(browser->diff_tv))->sel_start == (*(browser->diff_tv))->sel_end)
535 DisableItem(edit_menu, EDIT_MENU_COPY_ID);
536 else
537 EnableItem(edit_menu, EDIT_MENU_COPY_ID);
538
539 DisableItem(edit_menu, EDIT_MENU_PASTE_ID);
540
541 if ((*(browser->diff_tv))->nlines == 0)
542 DisableItem(edit_menu, EDIT_MENU_SELECT_ALL_ID);
543 else
544 EnableItem(edit_menu, EDIT_MENU_SELECT_ALL_ID);
545
546 HUnlock(browser->diff_tv);
547
548 if (browser->repo->nfiles == 0)
549 HiliteControl(browser->diff_button, 255);
550 else
551 HiliteControl(browser->diff_button, 0);
552
553 if (!browser->committer) {
554 EnableItem(repo_menu, REPO_MENU_ADD_FILE_ID);
555 EnableItem(repo_menu, REPO_MENU_DISCARD_CHANGES_ID);
556 #if 0
557 EnableItem(repo_menu, REPO_MENU_APPLY_DIFF_ID);
558 #endif
559 }
560
561 if (LGetSelect(true, &cell, browser->amendment_list)) {
562 EnableItem(amendment_menu, AMENDMENT_MENU_EDIT_ID);
563 EnableItem(amendment_menu, AMENDMENT_MENU_EXPORT_ID);
564 } else {
565 DisableItem(amendment_menu, AMENDMENT_MENU_EDIT_ID);
566 DisableItem(amendment_menu, AMENDMENT_MENU_EXPORT_ID);
567 }
568 }
569
570 void
571 browser_update(struct focusable *focusable, EventRecord *event)
572 {
573 struct browser *browser = (struct browser *)focusable->cookie;
574 Rect r;
575 short what = -1;
576 Handle t;
577
578 if (event != NULL)
579 what = event->what;
580
581 switch (what) {
582 case -1:
583 case updateEvt:
584 FillRect(&browser->win->portRect, fill_pattern);
585
586 HLock(browser->diff_tv);
587 r = (*(browser->diff_tv))->view;
588 HUnlock(browser->diff_tv);
589 EraseRect(&r);
590 TVUpdate(browser->diff_tv);
591 InsetRect(&r, -1, -1);
592 FrameRect(&r);
593
594 /*
595 * Under heavy memory pressure, List Manager seems to close our
596 * custom resources and then re-open them, clearing the custom
597 * addrs we set during setup. Make sure they're still alive before
598 * doing an update or we'll jump to NULL.
599 */
600 browser_patch_ldefs();
601
602 r = (*(browser->file_list))->rView;
603 InsetRect(&r, -1, -1);
604 FillRect(&r, white);
605 FrameRect(&r);
606 LUpdate(browser->win->visRgn, browser->file_list);
607
608 r = (*(browser->amendment_list))->rView;
609 InsetRect(&r, -1, -1);
610 FillRect(&r, white);
611 FrameRect(&r);
612 LUpdate(browser->win->visRgn, browser->amendment_list);
613
614 TextFont(DIFF_BUTTON_FONT);
615 TextFace(0);
616 TextSize(DIFF_BUTTON_FONT_SIZE);
617 UpdtControl(browser->win, browser->win->visRgn);
618
619 browser_update_menu(browser);
620
621 break;
622 }
623 }
624
625 void
626 browser_mouse_down(struct focusable *focusable, EventRecord *event)
627 {
628 Cell selected = { 0 }, now = { 0 };
629 Point p;
630 ControlHandle control;
631 Rect r;
632 struct browser *browser = (struct browser *)focusable->cookie;
633 struct repo_amendment *amendment = NULL;
634 short *selected_files = NULL, *now_selected_files = NULL;
635 short nselected = 0, nnow_selected = 0;
636 short val, adj, was_selected, i, data_len;
637
638 p = event->where;
639 GlobalToLocal(&p);
640
641 /* is it in diff text? */
642 HLock(browser->diff_tv);
643 r = (*(browser->diff_tv))->view;
644 HUnlock(browser->diff_tv);
645 if (PtInRect(p, &r)) {
646 TVClick(browser->diff_tv, p, ((event->modifiers & shiftKey) != 0));
647 browser_update_menu(browser);
648 return;
649 }
650
651 /* is it in file list? */
652 r = (*(browser->file_list))->rView;
653 r.right += SCROLLBAR_WIDTH;
654 if (PtInRect(p, &r)) {
655 /* store what is selected now */
656 nselected = browser_selected_file_ids(browser, &selected_files);
657
658 /* possibly highlight a new cell */
659 LClick(p, event->modifiers, browser->file_list);
660
661 nnow_selected = browser_selected_file_ids(browser,
662 &now_selected_files);
663
664 if (nnow_selected == 0) {
665 /* can't select nothing, select 'all files' */
666 selected.v = 0;
667 LSetSelect(true, selected, browser->file_list);
668 if (nselected != browser->repo->nfiles)
669 browser_filter_amendments(browser);
670 } else if (nselected != nnow_selected) {
671 browser_filter_amendments(browser);
672 } else {
673 for (i = 0; i < nselected; i++) {
674 if (selected_files[i] != now_selected_files[i]) {
675 browser_filter_amendments(browser);
676 break;
677 }
678 }
679 }
680
681 if (selected_files)
682 xfree(&selected_files);
683 if (now_selected_files)
684 xfree(&now_selected_files);
685
686 return;
687 }
688
689 /* is it in amendment list? */
690 r = (*(browser->amendment_list))->rView;
691 r.right += SCROLLBAR_WIDTH;
692 if (PtInRect(p, &r)) {
693 if (browser->repo == NULL)
694 return;
695
696 /* store what is selected now */
697 was_selected = LGetSelect(true, &selected, browser->amendment_list);
698
699 /* possibly highlight a new cell */
700 LClick(p, event->modifiers, browser->amendment_list);
701
702 if (LGetSelect(true, &now, browser->amendment_list) == false) {
703 if (was_selected)
704 browser_show_amendment(browser, NULL);
705 } else if (!was_selected || selected.v != now.v) {
706 if (was_selected)
707 LSetSelect(false, selected, browser->amendment_list);
708 LSetSelect(true, now, browser->amendment_list);
709
710 /* in a filtered list, amendments[now.v] won't be accurate */
711 data_len = sizeof(Ptr);
712 LGetCell(&amendment, &data_len, now, browser->amendment_list);
713 if (amendment == NULL && browser->repo->unloaded_amendments) {
714 repo_load_amendments(browser->repo, true);
715 browser_filter_amendments(browser);
716 progress(NULL);
717 } else
718 browser_show_amendment(browser, amendment);
719 }
720
721 return;
722 }
723
724 switch (FindControl(p, browser->win, &control)) {
725 case inButton:
726 TextFont(DIFF_BUTTON_FONT);
727 TextFace(0);
728 TextSize(DIFF_BUTTON_FONT_SIZE);
729 if (TrackControl(control, p, 0L) &&
730 control == browser->diff_button)
731 browser->state = BROWSER_STATE_OPEN_COMMITTER;
732 break;
733 case inUpButton:
734 case inDownButton:
735 case inPageUp:
736 case inPageDown:
737 if (control == browser->diff_scroller)
738 TVSetTrackScrollControl(browser->diff_tv);
739 else
740 break;
741 TrackControl(control, p, TVTrackScrollControl);
742 break;
743 case inThumb:
744 val = GetCtlValue(control);
745 if (TrackControl(control, p, 0L) == 0)
746 break;
747 adj = val - GetCtlValue(control);
748 if (adj != 0) {
749 val -= adj;
750 if (control == browser->diff_scroller)
751 TVScroll(browser->diff_tv, 0, adj);
752 SetCtlValue(control, val);
753 }
754 break;
755 }
756 }
757
758 bool
759 browser_handle_menu(struct focusable *focusable, short menu, short item)
760 {
761 struct browser *browser = (struct browser *)focusable->cookie;
762
763 switch (menu) {
764 case EDIT_MENU_ID:
765 switch (item) {
766 case EDIT_MENU_COPY_ID:
767 TVCopy(browser->diff_tv);
768 return true;
769 case EDIT_MENU_SELECT_ALL_ID:
770 TVSetSelect(browser->diff_tv, 0, LONG_MAX);
771 return true;
772 }
773 break;
774 case REPO_MENU_ID:
775 switch (item) {
776 case REPO_MENU_ADD_FILE_ID:
777 browser->state = BROWSER_STATE_ADD_FILE;
778 return true;
779 case REPO_MENU_DISCARD_CHANGES_ID:
780 browser->state = BROWSER_STATE_DISCARD_CHANGES;
781 return true;
782 case REPO_MENU_APPLY_DIFF_ID:
783 browser->state = BROWSER_STATE_APPLY_DIFF;
784 return true;
785 }
786 break;
787 case AMENDMENT_MENU_ID:
788 switch (item) {
789 case AMENDMENT_MENU_EDIT_ID:
790 browser->state = BROWSER_STATE_EDIT_AMENDMENT;
791 return true;
792 case AMENDMENT_MENU_EXPORT_ID:
793 browser->state = BROWSER_STATE_EXPORT_AMENDMENT;
794 return true;
795 }
796 break;
797 }
798
799 return false;
800 }