Download
jcs
/amend
/browser.c
(View History)
jcs repo: Set cursor to watch while we're loading a repo | Latest amendment: 118 on 2023-04-18 |
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 FILE_LIST_FONT geneva |
34 | #define FILE_LIST_FONT_SIZE 10 |
35 | #define DIFF_BUTTON_FONT geneva |
36 | #define DIFF_BUTTON_FONT_SIZE 12 |
37 | |
38 | #define PADDING 10 |
39 | |
40 | bool browser_close(struct focusable *focusable); |
41 | void browser_idle(struct focusable *focusable, EventRecord *event); |
42 | void browser_update_menu(struct browser *browser); |
43 | void browser_update(struct focusable *focusable, EventRecord *event); |
44 | void browser_show_amendment(struct browser *browser, |
45 | struct repo_amendment *amendment); |
46 | void browser_mouse_down(struct focusable *focusable, EventRecord *event); |
47 | bool browser_handle_menu(struct focusable *focusable, short menu, |
48 | short item); |
49 | |
50 | void browser_add_files(struct browser *browser); |
51 | void browser_filter_amendments(struct browser *browser); |
52 | void browser_discard_changes(struct browser *browser); |
53 | void browser_edit_amendment(struct browser *browser); |
54 | |
55 | Pattern fill_pattern; |
56 | Handle amendment_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 | /* file list */ |
153 | bounds.top = bounds.left = PADDING; |
154 | bounds.bottom = browser->win->portRect.bottom - |
155 | (browser->win->portRect.bottom * 0.6) - 2 - 20 - PADDING - PADDING; |
156 | bounds.right = 100; |
157 | |
158 | TextFont(FILE_LIST_FONT); |
159 | TextSize(FILE_LIST_FONT_SIZE); |
160 | browser->file_list = LNew(&bounds, &data_bounds, cell_size, 0, |
161 | browser->win, true, true, false, true); |
162 | if (!browser->file_list) |
163 | err(1, "Can't create file list"); |
164 | LAddColumn(1, 0, browser->file_list); |
165 | (*(browser->file_list))->selFlags = lNoNilHilite; |
166 | |
167 | /* diff button */ |
168 | bounds.top = bounds.bottom + PADDING; |
169 | bounds.bottom = bounds.top + 20; |
170 | bounds.right += SCROLLBAR_WIDTH; |
171 | TextFont(DIFF_BUTTON_FONT); |
172 | TextSize(DIFF_BUTTON_FONT_SIZE); |
173 | browser->diff_button = NewControl(browser->win, &bounds, |
174 | "\pGenerate Diff", true, 1, 1, 1, pushButProc | useWFont, 0L); |
175 | |
176 | /* amendment list */ |
177 | bounds.top = bounds.left = PADDING; |
178 | bounds.left = bounds.right + PADDING; |
179 | bounds.right = browser->win->portRect.right - SCROLLBAR_WIDTH - PADDING; |
180 | |
181 | if (amendment_list_ldef_h == NULL) { |
182 | /* dynamically patch list LDEF to point to our custom function */ |
183 | amendment_list_ldef_h = xGetResource('LDEF', AMENDMENT_LDEF_ID); |
184 | HLock(amendment_list_ldef_h); |
185 | ((tCodeStub *)*amendment_list_ldef_h)->addr = &amendment_list_ldef; |
186 | } |
187 | |
188 | cell_size.v = (FontHeight(geneva, 9) * 2) + 2; |
189 | browser->amendment_list = LNew(&bounds, &data_bounds, cell_size, |
190 | AMENDMENT_LDEF_ID, browser->win, true, true, false, true); |
191 | if (!browser->amendment_list) |
192 | err(1, "Can't create amendment list"); |
193 | LAddColumn(1, 0, browser->amendment_list); |
194 | (*(browser->amendment_list))->selFlags = lOnlyOne; |
195 | |
196 | /* diff text */ |
197 | bounds.top = (*browser->amendment_list)->rView.bottom + PADDING; |
198 | bounds.left = PADDING; |
199 | bounds.right = browser->win->portRect.right - SCROLLBAR_WIDTH - PADDING; |
200 | bounds.bottom = browser->win->portRect.bottom - PADDING; |
201 | te_bounds = bounds; |
202 | InsetRect(&te_bounds, 2, 2); |
203 | browser->diff_te = TEStylNew(&te_bounds, &bounds); |
204 | TEAutoView(true, browser->diff_te); |
205 | TETabEnable(browser->diff_te); |
206 | (*(browser->diff_te))->caretHook = NullCaretHook; |
207 | TEActivate(browser->diff_te); |
208 | |
209 | /* scrollbar for diff text */ |
210 | bounds.right = browser->win->portRect.right - PADDING; |
211 | bounds.left = bounds.right - SCROLLBAR_WIDTH; |
212 | bounds.bottom++; |
213 | bounds.top--; |
214 | browser->diff_scroller = NewControl(browser->win, &bounds, "\p", true, |
215 | 1, 1, 1, scrollBarProc, 0L); |
216 | |
217 | browser_update_menu(browser); |
218 | browser_add_files(browser); |
219 | UpdateScrollbarForTE(browser->win, browser->diff_scroller, |
220 | browser->diff_te, true); |
221 | |
222 | focusable = xmalloczero(sizeof(struct focusable), "focusable"); |
223 | focusable->cookie = browser; |
224 | focusable->win = browser->win; |
225 | focusable->idle = browser_idle; |
226 | focusable->update = browser_update; |
227 | focusable->mouse_down = browser_mouse_down; |
228 | focusable->menu = browser_handle_menu; |
229 | focusable->close = browser_close; |
230 | focusable_add(focusable); |
231 | |
232 | progress(NULL); |
233 | |
234 | SetCursor(&arrow); |
235 | |
236 | return browser; |
237 | } |
238 | |
239 | bool |
240 | browser_close(struct focusable *focusable) |
241 | { |
242 | struct browser *browser = (struct browser *)focusable->cookie; |
243 | |
244 | if (browser->committer) |
245 | browser_close_committer(browser); |
246 | |
247 | if (browser->repo) |
248 | repo_close(browser->repo); |
249 | |
250 | TEDispose(browser->diff_te); |
251 | DisposeWindow(browser->win); |
252 | |
253 | xfree(&browser); |
254 | |
255 | menu_defaults(); |
256 | |
257 | return true; |
258 | } |
259 | |
260 | void |
261 | browser_close_committer(struct browser *browser) |
262 | { |
263 | struct focusable *f; |
264 | |
265 | if (browser->committer) { |
266 | f = focusable_find(browser->committer->win); |
267 | if (f) |
268 | focusable_close(f); |
269 | } |
270 | } |
271 | |
272 | void |
273 | browser_add_files(struct browser *browser) |
274 | { |
275 | char filename[256]; |
276 | Cell cell = { 0, 0 }; |
277 | short i; |
278 | |
279 | LDoDraw(false, browser->file_list); |
280 | LDelRow(0, 0, browser->file_list); |
281 | browser_show_amendment(browser, NULL); |
282 | |
283 | TextFont(FILE_LIST_FONT); |
284 | TextSize(FILE_LIST_FONT_SIZE); |
285 | LAddRow(1, cell.v, browser->file_list); |
286 | LSetCell("[All Files]", 11, 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 | if (browser->repo->files[i]->flags & REPO_FILE_DELETED) |
294 | snprintf(filename, sizeof(filename), "~%s~", |
295 | browser->repo->files[i]->filename); |
296 | else |
297 | strlcpy(filename, browser->repo->files[i]->filename, |
298 | sizeof(filename)); |
299 | LSetCell(&filename, strlen(filename), cell, browser->file_list); |
300 | cell.v++; |
301 | } |
302 | |
303 | LDoDraw(true, browser->file_list); |
304 | |
305 | browser_filter_amendments(browser); |
306 | InvalRect(&browser->win->portRect); |
307 | } |
308 | |
309 | short |
310 | browser_is_all_files_selected(struct browser *browser) |
311 | { |
312 | Cell cell = { 0 }; |
313 | |
314 | if (browser->repo->nfiles == 0) |
315 | return 0; |
316 | |
317 | /* |
318 | * "All Files" is always cell.v=0, so if v=0 is selected or nothing |
319 | * is, select all files |
320 | */ |
321 | if (LGetSelect(false, &cell, browser->file_list) == true || |
322 | LGetSelect(true, &cell, browser->file_list) == false) |
323 | return 1; |
324 | |
325 | return 0; |
326 | } |
327 | |
328 | short |
329 | browser_selected_file_ids(struct browser *browser, short **selected_files) |
330 | { |
331 | Cell cell = { 0 }; |
332 | short nselected_files = 0, i; |
333 | |
334 | if (browser->repo->nfiles == 0) { |
335 | *selected_files = NULL; |
336 | return 0; |
337 | } |
338 | |
339 | *selected_files = xcalloc(browser->repo->nfiles, sizeof(short), |
340 | "selected_files"); |
341 | |
342 | if (browser_is_all_files_selected(browser)) { |
343 | nselected_files = browser->repo->nfiles; |
344 | for (i = 0; i < browser->repo->nfiles; i++) |
345 | (*selected_files)[i] = browser->repo->files[i]->id; |
346 | } else { |
347 | cell.v = 0; |
348 | while (LGetSelect(true, &cell, browser->file_list)) { |
349 | (*selected_files)[nselected_files] = |
350 | browser->repo->files[cell.v - 1]->id; |
351 | nselected_files++; |
352 | LNextCell(true, true, &cell, browser->file_list); |
353 | } |
354 | } |
355 | |
356 | return nselected_files; |
357 | } |
358 | |
359 | void |
360 | browser_filter_amendments(struct browser *browser) |
361 | { |
362 | Cell cell = { 0 }; |
363 | struct repo_amendment *amendment; |
364 | short i, j, k, add = 0; |
365 | short *selected_files = NULL; |
366 | short nselected_files = 0; |
367 | |
368 | LDoDraw(false, browser->amendment_list); |
369 | LDelRow(0, 0, browser->amendment_list); |
370 | browser_show_amendment(browser, NULL); |
371 | |
372 | /* fill in amendments for selected files */ |
373 | nselected_files = browser_selected_file_ids(browser, &selected_files); |
374 | cell.v = 0; |
375 | for (i = 0; i < browser->repo->namendments; i++) { |
376 | add = 0; |
377 | amendment = browser->repo->amendments[i]; |
378 | for (j = 0; j < amendment->nfiles; j++) { |
379 | for (k = 0; k < nselected_files; k++) { |
380 | if (selected_files[k] == amendment->file_ids[j]) { |
381 | add = 1; |
382 | break; |
383 | } |
384 | } |
385 | if (add) |
386 | break; |
387 | } |
388 | |
389 | if (!add) |
390 | continue; |
391 | |
392 | LAddRow(1, cell.v, browser->amendment_list); |
393 | LSetCell(&(browser->repo->amendments[i]), sizeof(Ptr), cell, |
394 | browser->amendment_list); |
395 | cell.v++; |
396 | } |
397 | |
398 | LDoDraw(true, browser->amendment_list); |
399 | InvalRect(&(*(browser->amendment_list))->rView); |
400 | |
401 | if (selected_files) |
402 | xfree(&selected_files); |
403 | } |
404 | |
405 | void |
406 | browser_show_amendment(struct browser *browser, |
407 | struct repo_amendment *amendment) |
408 | { |
409 | if (amendment == NULL) { |
410 | TESetText("", 0, browser->diff_te); |
411 | HLock(browser->diff_te); |
412 | InvalRect(&(*(browser->diff_te))->viewRect); |
413 | HUnlock(browser->diff_te); |
414 | } else { |
415 | SetCursor(*(GetCursor(watchCursor))); |
416 | repo_show_diff_text(browser->repo, amendment, browser->diff_te); |
417 | SetCursor(&arrow); |
418 | } |
419 | |
420 | UpdateScrollbarForTE(browser->win, browser->diff_scroller, |
421 | browser->diff_te, true); |
422 | browser_update_menu(browser); |
423 | } |
424 | |
425 | void |
426 | browser_discard_changes(struct browser *browser) |
427 | { |
428 | char buf[256], filename[256]; |
429 | struct repo_file *file; |
430 | short *selected = NULL; |
431 | short nselected = 0, i, error; |
432 | SFReply reply; |
433 | |
434 | nselected = browser_selected_file_ids(browser, &selected); |
435 | for (i = 0; i < nselected; i++) { |
436 | file = repo_file_with_id(browser->repo, selected[i]); |
437 | |
438 | snprintf(buf, sizeof(buf), "Save %s:", file->filename); |
439 | CtoPstr(buf); |
440 | |
441 | strlcpy(filename, file->filename, sizeof(filename)); |
442 | CtoPstr(filename); |
443 | |
444 | SFPutFile(centered_sfput_dialog(), buf, filename, NULL, &reply); |
445 | if (!reply.good) |
446 | break; |
447 | |
448 | error = repo_checkout_file(browser->repo, file, reply.vRefNum, |
449 | reply.fName); |
450 | if (error) |
451 | break; |
452 | } |
453 | } |
454 | |
455 | void |
456 | browser_export_amendment(struct browser *browser) |
457 | { |
458 | Cell selected = { 0 }; |
459 | struct repo_amendment *amendment; |
460 | short len; |
461 | SFReply reply; |
462 | char filename[255]; |
463 | |
464 | if (LGetSelect(true, &selected, browser->amendment_list) == false) |
465 | return; |
466 | |
467 | len = sizeof(Ptr); |
468 | LGetCell(&amendment, &len, selected, browser->amendment_list); |
469 | |
470 | snprintf(filename, sizeof(filename), "amendment_%d.diff", |
471 | amendment->id); |
472 | CtoPstr(filename); |
473 | |
474 | SFPutFile(centered_sfput_dialog(), "\pSave as:", filename, NULL, |
475 | &reply); |
476 | if (!reply.good) |
477 | return; |
478 | |
479 | repo_export_amendment(browser->repo, amendment, reply.vRefNum, |
480 | reply.fName); |
481 | } |
482 | |
483 | void |
484 | browser_apply_diff(struct browser *browser) |
485 | { |
486 | SFReply reply; |
487 | |
488 | SFGetFile(centered_sfget_dialog(), NULL, NULL, -1, NULL, NULL, &reply); |
489 | if (!reply.good) |
490 | return; |
491 | |
492 | patch_process(browser->repo, reply.fName, reply.vRefNum); |
493 | } |
494 | |
495 | void |
496 | browser_edit_amendment(struct browser *browser) |
497 | { |
498 | Cell selected = { 0 }; |
499 | struct repo_amendment *amendment; |
500 | short len; |
501 | |
502 | if (LGetSelect(true, &selected, browser->amendment_list) == false) |
503 | return; |
504 | |
505 | len = sizeof(Ptr); |
506 | LGetCell(&amendment, &len, selected, browser->amendment_list); |
507 | |
508 | editor_init(browser, amendment); |
509 | } |
510 | |
511 | void |
512 | browser_update_menu(struct browser *browser) |
513 | { |
514 | TERec *diff; |
515 | Cell cell = { 0, 0 }; |
516 | |
517 | TextFont(systemFont); |
518 | TextSize(12); |
519 | |
520 | HLock(browser->diff_te); |
521 | diff = *(browser->diff_te); |
522 | |
523 | DisableItem(edit_menu, EDIT_MENU_CUT_ID); |
524 | |
525 | if (diff->selStart == diff->selEnd) |
526 | DisableItem(edit_menu, EDIT_MENU_COPY_ID); |
527 | else |
528 | EnableItem(edit_menu, EDIT_MENU_COPY_ID); |
529 | |
530 | DisableItem(edit_menu, EDIT_MENU_PASTE_ID); |
531 | |
532 | if (diff->nLines == 0) |
533 | DisableItem(edit_menu, EDIT_MENU_SELECT_ALL_ID); |
534 | else |
535 | EnableItem(edit_menu, EDIT_MENU_SELECT_ALL_ID); |
536 | |
537 | HUnlock(browser->diff_te); |
538 | |
539 | if (browser->repo->nfiles == 0) |
540 | HiliteControl(browser->diff_button, 255); |
541 | else |
542 | HiliteControl(browser->diff_button, 0); |
543 | |
544 | if (!browser->committer) { |
545 | EnableItem(repo_menu, REPO_MENU_ADD_FILE_ID); |
546 | EnableItem(repo_menu, REPO_MENU_DISCARD_CHANGES_ID); |
547 | #if 0 |
548 | EnableItem(repo_menu, REPO_MENU_APPLY_DIFF_ID); |
549 | #endif |
550 | } |
551 | |
552 | if (LGetSelect(true, &cell, browser->amendment_list)) { |
553 | EnableItem(amendment_menu, AMENDMENT_MENU_EDIT_ID); |
554 | EnableItem(amendment_menu, AMENDMENT_MENU_EXPORT_ID); |
555 | } else { |
556 | DisableItem(amendment_menu, AMENDMENT_MENU_EDIT_ID); |
557 | DisableItem(amendment_menu, AMENDMENT_MENU_EXPORT_ID); |
558 | } |
559 | } |
560 | |
561 | void |
562 | browser_update(struct focusable *focusable, EventRecord *event) |
563 | { |
564 | struct browser *browser = (struct browser *)focusable->cookie; |
565 | Rect r; |
566 | short what = -1; |
567 | Handle t; |
568 | |
569 | if (event != NULL) |
570 | what = event->what; |
571 | |
572 | switch (what) { |
573 | case -1: |
574 | case updateEvt: |
575 | FillRect(&browser->win->portRect, fill_pattern); |
576 | |
577 | r = (*(browser->diff_te))->viewRect; |
578 | FillRect(&r, white); |
579 | TEUpdate(&r, browser->diff_te); |
580 | InsetRect(&r, -1, -1); |
581 | FrameRect(&r); |
582 | |
583 | r = (*(browser->file_list))->rView; |
584 | InsetRect(&r, -1, -1); |
585 | FillRect(&r, white); |
586 | FrameRect(&r); |
587 | TextFont(FILE_LIST_FONT); |
588 | TextSize(FILE_LIST_FONT_SIZE); |
589 | LUpdate(browser->win->visRgn, browser->file_list); |
590 | |
591 | r = (*(browser->amendment_list))->rView; |
592 | InsetRect(&r, -1, -1); |
593 | FillRect(&r, white); |
594 | FrameRect(&r); |
595 | |
596 | /* |
597 | * Under heavy memory pressure, List Manager seems to close our |
598 | * custom resource and then re-open it, clearing the custom addr |
599 | * we set during setup. Make sure it's still alive before doing |
600 | * an update or we'll jump to NULL. |
601 | */ |
602 | if (*amendment_list_ldef_h == NULL || |
603 | ((tCodeStub *)*amendment_list_ldef_h)->addr == 0) { |
604 | amendment_list_ldef_h = xGetResource('LDEF', AMENDMENT_LDEF_ID); |
605 | HLock(amendment_list_ldef_h); |
606 | ((tCodeStub *)*amendment_list_ldef_h)->addr = &amendment_list_ldef; |
607 | } |
608 | LUpdate(browser->win->visRgn, browser->amendment_list); |
609 | |
610 | TextFont(DIFF_BUTTON_FONT); |
611 | TextSize(DIFF_BUTTON_FONT_SIZE); |
612 | UpdtControl(browser->win, browser->win->visRgn); |
613 | |
614 | browser_update_menu(browser); |
615 | |
616 | break; |
617 | } |
618 | } |
619 | |
620 | void |
621 | browser_mouse_down(struct focusable *focusable, EventRecord *event) |
622 | { |
623 | Cell selected = { 0 }, now = { 0 }; |
624 | Point p; |
625 | ControlHandle control; |
626 | Rect r; |
627 | struct browser *browser = (struct browser *)focusable->cookie; |
628 | struct repo_amendment *amendment = NULL; |
629 | short *selected_files = NULL, *now_selected_files = NULL; |
630 | short nselected = 0, nnow_selected = 0; |
631 | short val, adj, was_selected, i, data_len; |
632 | |
633 | p = event->where; |
634 | GlobalToLocal(&p); |
635 | |
636 | /* is it in diff text? */ |
637 | r = (*(browser->diff_te))->viewRect; |
638 | if (PtInRect(p, &r)) { |
639 | TEClick(p, ((event->modifiers & shiftKey) != 0), browser->diff_te); |
640 | browser_update_menu(browser); |
641 | return; |
642 | } |
643 | |
644 | /* is it in file list? */ |
645 | r = (*(browser->file_list))->rView; |
646 | r.right += SCROLLBAR_WIDTH; |
647 | if (PtInRect(p, &r)) { |
648 | /* store what is selected now */ |
649 | nselected = browser_selected_file_ids(browser, &selected_files); |
650 | |
651 | /* in case any new items are redrawn in LClick */ |
652 | TextFont(FILE_LIST_FONT); |
653 | TextSize(FILE_LIST_FONT_SIZE); |
654 | |
655 | /* possibly highlight a new cell */ |
656 | LClick(p, event->modifiers, browser->file_list); |
657 | |
658 | nnow_selected = browser_selected_file_ids(browser, |
659 | &now_selected_files); |
660 | |
661 | if (nnow_selected == 0) { |
662 | /* can't select nothing, select 'all files' */ |
663 | selected.v = 0; |
664 | LSetSelect(true, selected, browser->file_list); |
665 | if (nselected != browser->repo->nfiles) |
666 | browser_filter_amendments(browser); |
667 | } else if (nselected != nnow_selected) { |
668 | browser_filter_amendments(browser); |
669 | } else { |
670 | for (i = 0; i < nselected; i++) { |
671 | if (selected_files[i] != now_selected_files[i]) { |
672 | browser_filter_amendments(browser); |
673 | break; |
674 | } |
675 | } |
676 | } |
677 | |
678 | if (selected_files) |
679 | xfree(&selected_files); |
680 | if (now_selected_files) |
681 | xfree(&now_selected_files); |
682 | |
683 | return; |
684 | } |
685 | |
686 | /* is it in amendment list? */ |
687 | r = (*(browser->amendment_list))->rView; |
688 | r.right += SCROLLBAR_WIDTH; |
689 | if (PtInRect(p, &r)) { |
690 | if (browser->repo == NULL) |
691 | return; |
692 | |
693 | /* store what is selected now */ |
694 | was_selected = LGetSelect(true, &selected, browser->amendment_list); |
695 | |
696 | /* possibly highlight a new cell */ |
697 | LClick(p, event->modifiers, browser->amendment_list); |
698 | |
699 | if (LGetSelect(true, &now, browser->amendment_list) == false) { |
700 | if (was_selected) |
701 | browser_show_amendment(browser, NULL); |
702 | } else if (!was_selected || selected.v != now.v) { |
703 | if (was_selected) |
704 | LSetSelect(false, selected, browser->amendment_list); |
705 | LSetSelect(true, now, browser->amendment_list); |
706 | |
707 | /* in a filtered list, amendments[now.v] won't be accurate */ |
708 | data_len = sizeof(Ptr); |
709 | LGetCell(&amendment, &data_len, now, browser->amendment_list); |
710 | browser_show_amendment(browser, amendment); |
711 | } |
712 | |
713 | return; |
714 | } |
715 | |
716 | switch (FindControl(p, browser->win, &control)) { |
717 | case inButton: |
718 | TextFont(DIFF_BUTTON_FONT); |
719 | TextSize(DIFF_BUTTON_FONT_SIZE); |
720 | if (TrackControl(control, p, 0L) && |
721 | control == browser->diff_button) |
722 | browser->state = BROWSER_STATE_OPEN_COMMITTER; |
723 | break; |
724 | case inUpButton: |
725 | case inDownButton: |
726 | case inPageUp: |
727 | case inPageDown: |
728 | if (control == browser->diff_scroller) |
729 | SetTrackControlTE(browser->diff_te); |
730 | else |
731 | break; |
732 | TrackControl(control, p, TrackMouseDownInControl); |
733 | break; |
734 | case inThumb: |
735 | val = GetCtlValue(control); |
736 | if (TrackControl(control, p, 0L) == 0) |
737 | break; |
738 | adj = val - GetCtlValue(control); |
739 | if (adj != 0) { |
740 | val -= adj; |
741 | if (control == browser->diff_scroller) |
742 | TEScroll(0, adj * TEGetHeight(0, 0, browser->diff_te), |
743 | browser->diff_te); |
744 | SetCtlValue(control, val); |
745 | } |
746 | break; |
747 | } |
748 | } |
749 | |
750 | bool |
751 | browser_handle_menu(struct focusable *focusable, short menu, short item) |
752 | { |
753 | struct browser *browser = (struct browser *)focusable->cookie; |
754 | |
755 | switch (menu) { |
756 | case EDIT_MENU_ID: |
757 | switch (item) { |
758 | case EDIT_MENU_COPY_ID: |
759 | TECopy(browser->diff_te); |
760 | return true; |
761 | case EDIT_MENU_SELECT_ALL_ID: |
762 | TESetSelect(0, 1024 * 32, browser->diff_te); |
763 | return true; |
764 | } |
765 | break; |
766 | case REPO_MENU_ID: |
767 | switch (item) { |
768 | case REPO_MENU_ADD_FILE_ID: |
769 | browser->state = BROWSER_STATE_ADD_FILE; |
770 | return true; |
771 | case REPO_MENU_DISCARD_CHANGES_ID: |
772 | browser->state = BROWSER_STATE_DISCARD_CHANGES; |
773 | return true; |
774 | case REPO_MENU_APPLY_DIFF_ID: |
775 | browser->state = BROWSER_STATE_APPLY_DIFF; |
776 | return true; |
777 | } |
778 | break; |
779 | case AMENDMENT_MENU_ID: |
780 | switch (item) { |
781 | case AMENDMENT_MENU_EDIT_ID: |
782 | browser->state = BROWSER_STATE_EDIT_AMENDMENT; |
783 | return true; |
784 | case AMENDMENT_MENU_EXPORT_ID: |
785 | browser->state = BROWSER_STATE_EXPORT_AMENDMENT; |
786 | return true; |
787 | } |
788 | break; |
789 | } |
790 | |
791 | return false; |
792 | } |