Download
jcs
/amend
/browser.c
(View History)
jcs browser: Use a custom LDEF for file list to cross out deleted files | Latest amendment: 253 on 2023-11-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 "tetab.h" |
31 | #include "util.h" |
32 | |
33 | #define DIFF_BUTTON_FONT geneva |
34 | #define DIFF_BUTTON_FONT_SIZE 12 |
35 | |
36 | #define PADDING 10 |
37 | |
38 | bool browser_close(struct focusable *focusable); |
39 | void browser_idle(struct focusable *focusable, EventRecord *event); |
40 | void browser_update_menu(struct browser *browser); |
41 | void browser_update(struct focusable *focusable, EventRecord *event); |
42 | void browser_patch_ldefs(void); |
43 | void browser_show_amendment(struct browser *browser, |
44 | struct repo_amendment *amendment); |
45 | void browser_mouse_down(struct focusable *focusable, EventRecord *event); |
46 | bool browser_handle_menu(struct focusable *focusable, short menu, |
47 | short item); |
48 | |
49 | void browser_add_files(struct browser *browser); |
50 | void browser_filter_amendments(struct browser *browser); |
51 | void browser_discard_changes(struct browser *browser); |
52 | void browser_edit_amendment(struct browser *browser); |
53 | |
54 | Pattern fill_pattern; |
55 | Handle amendment_list_ldef_h = NULL; |
56 | Handle file_list_ldef_h = NULL; |
57 | |
58 | void |
59 | browser_idle(struct focusable *focusable, EventRecord *event) |
60 | { |
61 | struct browser *browser = (struct browser *)focusable->cookie; |
62 | |
63 | switch (browser->state) { |
64 | case BROWSER_STATE_IDLE: |
65 | if (browser->need_refresh) { |
66 | browser->need_refresh = false; |
67 | browser->state = BROWSER_STATE_UPDATE_AMENDMENT_LIST; |
68 | } |
69 | break; |
70 | case BROWSER_STATE_ADD_FILE: |
71 | if (repo_add_file(browser->repo) == NULL) |
72 | browser->state = BROWSER_STATE_IDLE; |
73 | else |
74 | browser->state = BROWSER_STATE_UPDATE_FILE_LIST; |
75 | break; |
76 | case BROWSER_STATE_UPDATE_FILE_LIST: |
77 | browser_add_files(browser); |
78 | browser->state = BROWSER_STATE_IDLE; |
79 | break; |
80 | case BROWSER_STATE_UPDATE_AMENDMENT_LIST: |
81 | browser_filter_amendments(browser); |
82 | browser->state = BROWSER_STATE_IDLE; |
83 | break; |
84 | case BROWSER_STATE_OPEN_COMMITTER: |
85 | committer_init(browser); |
86 | browser->state = BROWSER_STATE_WAITING_FOR_COMMITTER; |
87 | break; |
88 | case BROWSER_STATE_WAITING_FOR_COMMITTER: |
89 | break; |
90 | case BROWSER_STATE_DISCARD_CHANGES: |
91 | browser_discard_changes(browser); |
92 | browser->state = BROWSER_STATE_IDLE; |
93 | break; |
94 | case BROWSER_STATE_EXPORT_AMENDMENT: |
95 | browser_export_amendment(browser); |
96 | browser->state = BROWSER_STATE_IDLE; |
97 | break; |
98 | case BROWSER_STATE_APPLY_DIFF: |
99 | browser_apply_diff(browser); |
100 | browser->state = BROWSER_STATE_IDLE; |
101 | break; |
102 | case BROWSER_STATE_EDIT_AMENDMENT: |
103 | browser_edit_amendment(browser); |
104 | browser->state = BROWSER_STATE_IDLE; |
105 | break; |
106 | } |
107 | } |
108 | |
109 | struct browser * |
110 | browser_init(struct repo *repo) |
111 | { |
112 | char title[256], filename[256], *justfilename; |
113 | struct browser *browser; |
114 | struct focusable *focusable; |
115 | Rect bounds = { 0 }, te_bounds = { 0 }; |
116 | Rect data_bounds = { 0, 0, 0, 1 }; /* tlbr */ |
117 | Point cell_size = { 0, 0 }; |
118 | Cell cell = { 0 }; |
119 | short width, height; |
120 | |
121 | browser = xmalloczero(sizeof(struct browser), "browser"); |
122 | browser->state = BROWSER_STATE_IDLE; |
123 | browser->repo = repo; |
124 | |
125 | GetIndPattern(&fill_pattern, sysPatListID, 22); |
126 | |
127 | /* main window */ |
128 | width = screenBits.bounds.right - screenBits.bounds.left - PADDING; |
129 | if (width > 540) |
130 | width = 540; |
131 | height = screenBits.bounds.bottom - screenBits.bounds.top - |
132 | PADDING - (GetMBarHeight() * 2); |
133 | if (height > 350) |
134 | height = 350; |
135 | center_in_screen(width, height, true, &bounds); |
136 | |
137 | memcpy(filename, browser->repo->bile->filename, sizeof(filename)); |
138 | PtoCstr(filename); |
139 | justfilename = strrchr(filename, ':'); |
140 | if (justfilename == NULL) |
141 | justfilename = (char *)&filename; |
142 | else |
143 | justfilename++; |
144 | snprintf(title, sizeof(title), "%s: %s", PROGRAM_NAME, justfilename); |
145 | CtoPstr(title); |
146 | browser->win = NewWindow(0L, &bounds, title, false, noGrowDocProc, |
147 | (WindowPtr)-1L, true, 0); |
148 | if (!browser->win) |
149 | err(1, "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 | err(1, "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 | err(1, "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 | te_bounds = bounds; |
198 | InsetRect(&te_bounds, 2, 2); |
199 | browser->diff_te = TEStylNew(&te_bounds, &bounds); |
200 | TEAutoView(true, browser->diff_te); |
201 | TETabEnable(browser->diff_te); |
202 | (*(browser->diff_te))->caretHook = NullCaretHook; |
203 | TEActivate(browser->diff_te); |
204 | |
205 | /* scrollbar for diff text */ |
206 | bounds.right = browser->win->portRect.right - PADDING; |
207 | bounds.left = bounds.right - SCROLLBAR_WIDTH; |
208 | bounds.bottom++; |
209 | bounds.top--; |
210 | browser->diff_scroller = NewControl(browser->win, &bounds, "\p", true, |
211 | 1, 1, 1, scrollBarProc, 0L); |
212 | |
213 | browser_update_menu(browser); |
214 | browser_add_files(browser); |
215 | UpdateScrollbarForTE(browser->win, browser->diff_scroller, |
216 | browser->diff_te, true); |
217 | |
218 | focusable = xmalloczero(sizeof(struct focusable), "focusable"); |
219 | focusable->cookie = browser; |
220 | focusable->win = browser->win; |
221 | focusable->idle = browser_idle; |
222 | focusable->update = browser_update; |
223 | focusable->mouse_down = browser_mouse_down; |
224 | focusable->menu = browser_handle_menu; |
225 | focusable->close = browser_close; |
226 | focusable_add(focusable); |
227 | |
228 | progress(NULL); |
229 | |
230 | SetCursor(&arrow); |
231 | |
232 | return browser; |
233 | } |
234 | |
235 | bool |
236 | browser_close(struct focusable *focusable) |
237 | { |
238 | struct browser *browser = (struct browser *)focusable->cookie; |
239 | |
240 | if (browser->committer) |
241 | browser_close_committer(browser); |
242 | |
243 | if (browser->repo) |
244 | repo_close(browser->repo); |
245 | |
246 | TEDispose(browser->diff_te); |
247 | DisposeWindow(browser->win); |
248 | |
249 | xfree(&browser); |
250 | |
251 | menu_defaults(); |
252 | |
253 | return true; |
254 | } |
255 | |
256 | void |
257 | browser_close_committer(struct browser *browser) |
258 | { |
259 | struct focusable *f; |
260 | |
261 | if (browser->committer) { |
262 | f = focusable_find(browser->committer->win); |
263 | if (f) |
264 | focusable_close(f); |
265 | } |
266 | } |
267 | |
268 | void |
269 | browser_add_files(struct browser *browser) |
270 | { |
271 | Cell cell = { 0, 0 }; |
272 | short i; |
273 | |
274 | LDoDraw(false, browser->file_list); |
275 | LDelRow(0, 0, browser->file_list); |
276 | browser_show_amendment(browser, NULL); |
277 | |
278 | LAddRow(1, cell.v, browser->file_list); |
279 | /* will become "[All Files]" */ |
280 | LSetCell(NULL, sizeof(Ptr), cell, browser->file_list); |
281 | LSetSelect(true, cell, browser->file_list); |
282 | cell.v++; |
283 | |
284 | /* fill in files */ |
285 | for (i = 0; i < browser->repo->nfiles; i++) { |
286 | LAddRow(1, cell.v, browser->file_list); |
287 | LSetCell(&browser->repo->files[i], sizeof(Ptr), cell, |
288 | browser->file_list); |
289 | cell.v++; |
290 | } |
291 | |
292 | LDoDraw(true, browser->file_list); |
293 | |
294 | browser_filter_amendments(browser); |
295 | InvalRect(&browser->win->portRect); |
296 | } |
297 | |
298 | short |
299 | browser_is_all_files_selected(struct browser *browser) |
300 | { |
301 | Cell cell = { 0 }; |
302 | |
303 | if (browser->repo->nfiles == 0) |
304 | return 0; |
305 | |
306 | /* |
307 | * "All Files" is always cell.v=0, so if v=0 is selected or nothing |
308 | * is, select all files |
309 | */ |
310 | if (LGetSelect(false, &cell, browser->file_list) == true || |
311 | LGetSelect(true, &cell, browser->file_list) == false) |
312 | return 1; |
313 | |
314 | return 0; |
315 | } |
316 | |
317 | short |
318 | browser_selected_file_ids(struct browser *browser, short **selected_files) |
319 | { |
320 | Cell cell = { 0 }; |
321 | short nselected_files = 0, i; |
322 | |
323 | if (browser->repo->nfiles == 0) { |
324 | *selected_files = NULL; |
325 | return 0; |
326 | } |
327 | |
328 | *selected_files = xcalloc(browser->repo->nfiles, sizeof(short), |
329 | "selected_files"); |
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 | if (amendment == NULL) { |
406 | TESetText("", 0, browser->diff_te); |
407 | HLock(browser->diff_te); |
408 | InvalRect(&(*(browser->diff_te))->viewRect); |
409 | HUnlock(browser->diff_te); |
410 | } else { |
411 | SetCursor(*(GetCursor(watchCursor))); |
412 | repo_show_diff_text(browser->repo, amendment, browser->diff_te); |
413 | SetCursor(&arrow); |
414 | } |
415 | |
416 | UpdateScrollbarForTE(browser->win, browser->diff_scroller, |
417 | browser->diff_te, true); |
418 | browser_update_menu(browser); |
419 | } |
420 | |
421 | void |
422 | browser_discard_changes(struct browser *browser) |
423 | { |
424 | char buf[256], filename[256]; |
425 | struct repo_file *file; |
426 | short *selected = NULL; |
427 | short nselected = 0, i, error; |
428 | SFReply reply; |
429 | |
430 | nselected = browser_selected_file_ids(browser, &selected); |
431 | for (i = 0; i < nselected; i++) { |
432 | file = repo_file_with_id(browser->repo, selected[i]); |
433 | |
434 | snprintf(buf, sizeof(buf), "Save %s:", file->filename); |
435 | CtoPstr(buf); |
436 | |
437 | strlcpy(filename, file->filename, sizeof(filename)); |
438 | CtoPstr(filename); |
439 | |
440 | SFPutFile(centered_sfput_dialog(), buf, filename, NULL, &reply); |
441 | if (!reply.good) |
442 | break; |
443 | |
444 | error = repo_checkout_file(browser->repo, file, reply.vRefNum, |
445 | reply.fName); |
446 | if (error) |
447 | break; |
448 | } |
449 | } |
450 | |
451 | void |
452 | browser_export_amendment(struct browser *browser) |
453 | { |
454 | Cell selected = { 0 }; |
455 | struct repo_amendment *amendment; |
456 | short len; |
457 | SFReply reply; |
458 | char filename[255]; |
459 | |
460 | if (LGetSelect(true, &selected, browser->amendment_list) == false) |
461 | return; |
462 | |
463 | len = sizeof(Ptr); |
464 | LGetCell(&amendment, &len, selected, browser->amendment_list); |
465 | |
466 | snprintf(filename, sizeof(filename), "amendment_%d.diff", |
467 | amendment->id); |
468 | CtoPstr(filename); |
469 | |
470 | SFPutFile(centered_sfput_dialog(), "\pSave as:", filename, NULL, |
471 | &reply); |
472 | if (!reply.good) |
473 | return; |
474 | |
475 | repo_export_amendment(browser->repo, amendment, reply.vRefNum, |
476 | reply.fName); |
477 | } |
478 | |
479 | void |
480 | browser_apply_diff(struct browser *browser) |
481 | { |
482 | SFReply reply; |
483 | |
484 | SFGetFile(centered_sfget_dialog(), NULL, NULL, -1, NULL, NULL, &reply); |
485 | if (!reply.good) |
486 | return; |
487 | |
488 | patch_process(browser->repo, reply.fName, reply.vRefNum); |
489 | } |
490 | |
491 | void |
492 | browser_edit_amendment(struct browser *browser) |
493 | { |
494 | Cell selected = { 0 }; |
495 | struct repo_amendment *amendment; |
496 | short len; |
497 | |
498 | if (LGetSelect(true, &selected, browser->amendment_list) == false) |
499 | return; |
500 | |
501 | len = sizeof(Ptr); |
502 | LGetCell(&amendment, &len, selected, browser->amendment_list); |
503 | |
504 | editor_init(browser, amendment); |
505 | } |
506 | |
507 | void |
508 | browser_patch_ldefs(void) |
509 | { |
510 | /* dynamically patch LDEFs to point to our custom functions */ |
511 | if (file_list_ldef_h == NULL || *file_list_ldef_h == NULL || |
512 | ((tCodeStub *)*file_list_ldef_h)->addr == 0) { |
513 | file_list_ldef_h = xGetResource('LDEF', FILE_LDEF_ID); |
514 | HLock(file_list_ldef_h); |
515 | ((tCodeStub *)*file_list_ldef_h)->addr = &file_list_ldef; |
516 | } |
517 | |
518 | if (amendment_list_ldef_h == NULL || *amendment_list_ldef_h == NULL || |
519 | ((tCodeStub *)*amendment_list_ldef_h)->addr == 0) { |
520 | amendment_list_ldef_h = xGetResource('LDEF', AMENDMENT_LDEF_ID); |
521 | HLock(amendment_list_ldef_h); |
522 | ((tCodeStub *)*amendment_list_ldef_h)->addr = &amendment_list_ldef; |
523 | } |
524 | } |
525 | |
526 | void |
527 | browser_update_menu(struct browser *browser) |
528 | { |
529 | TERec *diff; |
530 | Cell cell = { 0, 0 }; |
531 | |
532 | TextFont(systemFont); |
533 | TextFace(0); |
534 | TextSize(12); |
535 | |
536 | HLock(browser->diff_te); |
537 | diff = *(browser->diff_te); |
538 | |
539 | DisableItem(edit_menu, EDIT_MENU_CUT_ID); |
540 | |
541 | if (diff->selStart == diff->selEnd) |
542 | DisableItem(edit_menu, EDIT_MENU_COPY_ID); |
543 | else |
544 | EnableItem(edit_menu, EDIT_MENU_COPY_ID); |
545 | |
546 | DisableItem(edit_menu, EDIT_MENU_PASTE_ID); |
547 | |
548 | if (diff->nLines == 0) |
549 | DisableItem(edit_menu, EDIT_MENU_SELECT_ALL_ID); |
550 | else |
551 | EnableItem(edit_menu, EDIT_MENU_SELECT_ALL_ID); |
552 | |
553 | HUnlock(browser->diff_te); |
554 | |
555 | if (browser->repo->nfiles == 0) |
556 | HiliteControl(browser->diff_button, 255); |
557 | else |
558 | HiliteControl(browser->diff_button, 0); |
559 | |
560 | if (!browser->committer) { |
561 | EnableItem(repo_menu, REPO_MENU_ADD_FILE_ID); |
562 | EnableItem(repo_menu, REPO_MENU_DISCARD_CHANGES_ID); |
563 | #if 0 |
564 | EnableItem(repo_menu, REPO_MENU_APPLY_DIFF_ID); |
565 | #endif |
566 | } |
567 | |
568 | if (LGetSelect(true, &cell, browser->amendment_list)) { |
569 | EnableItem(amendment_menu, AMENDMENT_MENU_EDIT_ID); |
570 | EnableItem(amendment_menu, AMENDMENT_MENU_EXPORT_ID); |
571 | } else { |
572 | DisableItem(amendment_menu, AMENDMENT_MENU_EDIT_ID); |
573 | DisableItem(amendment_menu, AMENDMENT_MENU_EXPORT_ID); |
574 | } |
575 | } |
576 | |
577 | void |
578 | browser_update(struct focusable *focusable, EventRecord *event) |
579 | { |
580 | struct browser *browser = (struct browser *)focusable->cookie; |
581 | Rect r; |
582 | short what = -1; |
583 | Handle t; |
584 | |
585 | if (event != NULL) |
586 | what = event->what; |
587 | |
588 | switch (what) { |
589 | case -1: |
590 | case updateEvt: |
591 | FillRect(&browser->win->portRect, fill_pattern); |
592 | |
593 | r = (*(browser->diff_te))->viewRect; |
594 | FillRect(&r, white); |
595 | TEUpdate(&r, browser->diff_te); |
596 | InsetRect(&r, -1, -1); |
597 | FrameRect(&r); |
598 | |
599 | /* |
600 | * Under heavy memory pressure, List Manager seems to close our |
601 | * custom resources and then re-open them, clearing the custom |
602 | * addrs we set during setup. Make sure they're still alive before |
603 | * doing an update or we'll jump to NULL. |
604 | */ |
605 | browser_patch_ldefs(); |
606 | |
607 | r = (*(browser->file_list))->rView; |
608 | InsetRect(&r, -1, -1); |
609 | FillRect(&r, white); |
610 | FrameRect(&r); |
611 | LUpdate(browser->win->visRgn, browser->file_list); |
612 | |
613 | r = (*(browser->amendment_list))->rView; |
614 | InsetRect(&r, -1, -1); |
615 | FillRect(&r, white); |
616 | FrameRect(&r); |
617 | LUpdate(browser->win->visRgn, browser->amendment_list); |
618 | |
619 | TextFont(DIFF_BUTTON_FONT); |
620 | TextFace(0); |
621 | TextSize(DIFF_BUTTON_FONT_SIZE); |
622 | UpdtControl(browser->win, browser->win->visRgn); |
623 | |
624 | browser_update_menu(browser); |
625 | |
626 | break; |
627 | } |
628 | } |
629 | |
630 | void |
631 | browser_mouse_down(struct focusable *focusable, EventRecord *event) |
632 | { |
633 | Cell selected = { 0 }, now = { 0 }; |
634 | Point p; |
635 | ControlHandle control; |
636 | Rect r; |
637 | struct browser *browser = (struct browser *)focusable->cookie; |
638 | struct repo_amendment *amendment = NULL; |
639 | short *selected_files = NULL, *now_selected_files = NULL; |
640 | short nselected = 0, nnow_selected = 0; |
641 | short val, adj, was_selected, i, data_len; |
642 | |
643 | p = event->where; |
644 | GlobalToLocal(&p); |
645 | |
646 | /* is it in diff text? */ |
647 | r = (*(browser->diff_te))->viewRect; |
648 | if (PtInRect(p, &r)) { |
649 | TEClick(p, ((event->modifiers & shiftKey) != 0), browser->diff_te); |
650 | browser_update_menu(browser); |
651 | return; |
652 | } |
653 | |
654 | /* is it in file list? */ |
655 | r = (*(browser->file_list))->rView; |
656 | r.right += SCROLLBAR_WIDTH; |
657 | if (PtInRect(p, &r)) { |
658 | /* store what is selected now */ |
659 | nselected = browser_selected_file_ids(browser, &selected_files); |
660 | |
661 | /* possibly highlight a new cell */ |
662 | LClick(p, event->modifiers, browser->file_list); |
663 | |
664 | nnow_selected = browser_selected_file_ids(browser, |
665 | &now_selected_files); |
666 | |
667 | if (nnow_selected == 0) { |
668 | /* can't select nothing, select 'all files' */ |
669 | selected.v = 0; |
670 | LSetSelect(true, selected, browser->file_list); |
671 | if (nselected != browser->repo->nfiles) |
672 | browser_filter_amendments(browser); |
673 | } else if (nselected != nnow_selected) { |
674 | browser_filter_amendments(browser); |
675 | } else { |
676 | for (i = 0; i < nselected; i++) { |
677 | if (selected_files[i] != now_selected_files[i]) { |
678 | browser_filter_amendments(browser); |
679 | break; |
680 | } |
681 | } |
682 | } |
683 | |
684 | if (selected_files) |
685 | xfree(&selected_files); |
686 | if (now_selected_files) |
687 | xfree(&now_selected_files); |
688 | |
689 | return; |
690 | } |
691 | |
692 | /* is it in amendment list? */ |
693 | r = (*(browser->amendment_list))->rView; |
694 | r.right += SCROLLBAR_WIDTH; |
695 | if (PtInRect(p, &r)) { |
696 | if (browser->repo == NULL) |
697 | return; |
698 | |
699 | /* store what is selected now */ |
700 | was_selected = LGetSelect(true, &selected, browser->amendment_list); |
701 | |
702 | /* possibly highlight a new cell */ |
703 | LClick(p, event->modifiers, browser->amendment_list); |
704 | |
705 | if (LGetSelect(true, &now, browser->amendment_list) == false) { |
706 | if (was_selected) |
707 | browser_show_amendment(browser, NULL); |
708 | } else if (!was_selected || selected.v != now.v) { |
709 | if (was_selected) |
710 | LSetSelect(false, selected, browser->amendment_list); |
711 | LSetSelect(true, now, browser->amendment_list); |
712 | |
713 | /* in a filtered list, amendments[now.v] won't be accurate */ |
714 | data_len = sizeof(Ptr); |
715 | LGetCell(&amendment, &data_len, now, browser->amendment_list); |
716 | if (amendment == NULL && browser->repo->unloaded_amendments) { |
717 | repo_load_amendments(browser->repo, true); |
718 | browser_filter_amendments(browser); |
719 | progress(NULL); |
720 | } else |
721 | browser_show_amendment(browser, amendment); |
722 | } |
723 | |
724 | return; |
725 | } |
726 | |
727 | switch (FindControl(p, browser->win, &control)) { |
728 | case inButton: |
729 | TextFont(DIFF_BUTTON_FONT); |
730 | TextFace(0); |
731 | TextSize(DIFF_BUTTON_FONT_SIZE); |
732 | if (TrackControl(control, p, 0L) && |
733 | control == browser->diff_button) |
734 | browser->state = BROWSER_STATE_OPEN_COMMITTER; |
735 | break; |
736 | case inUpButton: |
737 | case inDownButton: |
738 | case inPageUp: |
739 | case inPageDown: |
740 | if (control == browser->diff_scroller) |
741 | SetTrackControlTE(browser->diff_te); |
742 | else |
743 | break; |
744 | TrackControl(control, p, TrackMouseDownInControl); |
745 | break; |
746 | case inThumb: |
747 | val = GetCtlValue(control); |
748 | if (TrackControl(control, p, 0L) == 0) |
749 | break; |
750 | adj = val - GetCtlValue(control); |
751 | if (adj != 0) { |
752 | val -= adj; |
753 | if (control == browser->diff_scroller) |
754 | TEScroll(0, adj * TEGetHeight(0, 0, browser->diff_te), |
755 | browser->diff_te); |
756 | SetCtlValue(control, val); |
757 | } |
758 | break; |
759 | } |
760 | } |
761 | |
762 | bool |
763 | browser_handle_menu(struct focusable *focusable, short menu, short item) |
764 | { |
765 | struct browser *browser = (struct browser *)focusable->cookie; |
766 | |
767 | switch (menu) { |
768 | case EDIT_MENU_ID: |
769 | switch (item) { |
770 | case EDIT_MENU_COPY_ID: |
771 | TECopy(browser->diff_te); |
772 | return true; |
773 | case EDIT_MENU_SELECT_ALL_ID: |
774 | TESetSelect(0, 1024 * 32, browser->diff_te); |
775 | return true; |
776 | } |
777 | break; |
778 | case REPO_MENU_ID: |
779 | switch (item) { |
780 | case REPO_MENU_ADD_FILE_ID: |
781 | browser->state = BROWSER_STATE_ADD_FILE; |
782 | return true; |
783 | case REPO_MENU_DISCARD_CHANGES_ID: |
784 | browser->state = BROWSER_STATE_DISCARD_CHANGES; |
785 | return true; |
786 | case REPO_MENU_APPLY_DIFF_ID: |
787 | browser->state = BROWSER_STATE_APPLY_DIFF; |
788 | return true; |
789 | } |
790 | break; |
791 | case AMENDMENT_MENU_ID: |
792 | switch (item) { |
793 | case AMENDMENT_MENU_EDIT_ID: |
794 | browser->state = BROWSER_STATE_EDIT_AMENDMENT; |
795 | return true; |
796 | case AMENDMENT_MENU_EXPORT_ID: |
797 | browser->state = BROWSER_STATE_EXPORT_AMENDMENT; |
798 | return true; |
799 | } |
800 | break; |
801 | } |
802 | |
803 | return false; |
804 | } |