AmendHub

Download

jcs

/

subtext

/

settings.c

 

(View History)

jcs   settings: Fix alignment of scrollbars in editor window Latest amendment: 287 on 2022-11-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 <stdio.h>
18 #include <string.h>
19
20 #include "subtext.h"
21 #include "db.h"
22 #include "focusable.h"
23 #include "main_menu.h"
24 #include "settings.h"
25 #include "tcp.h" /* for long2ip/ip2long */
26 #include "tetab.h"
27 #include "util.h"
28
29 struct view_editor {
30 WindowPtr win;
31 size_t view_id;
32 TEHandle te;
33 ControlHandle vert_scroller;
34 ControlHandle horiz_scroller;
35 ControlHandle save_button;
36 };
37
38 void view_editor_key_down(struct focusable *focusable, EventRecord *event);
39 void view_editor_mouse_down(struct focusable *focusable,
40 EventRecord *event);
41 void view_editor_update(struct focusable *focusable, EventRecord *event);
42 void view_editor_close(struct focusable *focusable, EventRecord *event);
43 void view_editor_save(struct focusable *focusable, EventRecord *event);
44
45 short
46 struct_editor(struct session *s, const struct struct_field *fields,
47 const size_t nfields, void *data, size_t dsize, void **result,
48 char *title, char *prompt)
49 {
50 Handle ihandle;
51 const struct struct_field *sf;
52 long lval;
53 char co, initial[20];
54 char *input = NULL, *new_data;
55 short itype, hit, n, sval;
56 bool found, any_changes = false;
57 unsigned short c;
58
59 session_printf(s, "{{B}}Editor: %s{{/B}}\r\n", title);
60 session_flush(s);
61
62 new_data = xmalloc(dsize, "struct_editor");
63 memcpy(new_data, data, dsize);
64
65 print_options:
66 for (n = 0; n < nfields; n++) {
67 sf = &fields[n];
68
69 if (n > 9)
70 co = 'A' + (n - 10);
71 else
72 co = '0' + n;
73
74 session_printf(s, "{{B}}%c{{/B}}: %s [", co, sf->name);
75
76 switch (sf->type) {
77 case CONFIG_TYPE_STRING:
78 session_printf(s, "%s", new_data + sf->off);
79 break;
80 case CONFIG_TYPE_SHORT:
81 sval = CHARS_TO_SHORT(new_data[sf->off], new_data[sf->off + 1]);
82 session_printf(s, "%d", sval);
83 break;
84 case CONFIG_TYPE_LONG:
85 lval = CHARS_TO_LONG(new_data[sf->off], new_data[sf->off + 1],
86 new_data[sf->off + 2], new_data[sf->off + 3]);
87 session_printf(s, "%ld", lval);
88 break;
89 case CONFIG_TYPE_BOOLEAN:
90 session_printf(s, "%s",
91 new_data[sf->off] == 0 ? "false" : "true");
92 break;
93 case CONFIG_TYPE_IP: {
94 char ip_str[16];
95 lval = CHARS_TO_LONG(new_data[sf->off], new_data[sf->off + 1],
96 new_data[sf->off + 2], new_data[sf->off + 3]);
97 if (lval == 0)
98 session_printf(s, "0.0.0.0");
99 else {
100 long2ip(lval, ip_str);
101 session_printf(s, "%s", ip_str);
102 }
103 break;
104 }
105 }
106
107 session_printf(s, "]\r\n");
108 }
109
110 session_printf(s, "{{B}}S{{/B}}: Save and return to main menu\r\n");
111 session_printf(s, "{{B}}Q{{/B}}: Discard changes and return to main menu\r\n");
112 session_printf(s, "{{B}}?{{/B}}: Print menu of options\r\n");
113
114 for (;;) {
115 session_printf(s, "{{B}}%s>{{/B}} ", prompt);
116 session_flush(s);
117
118 get_menu_option:
119 c = session_input_char(s);
120 if (s->ending) {
121 any_changes = false;
122 goto done;
123 }
124 if (c == '\r' || c == 0 || c > 255)
125 goto get_menu_option;
126
127 session_printf(s, "%c\r\n", c);
128 session_flush(s);
129
130 if (c == '?')
131 goto print_options;
132 if (c == 'S' || c == 's')
133 break;
134 if (c == 'Q' || c == 'q') {
135 any_changes = false;
136 goto done;
137 }
138
139 found = false;
140 sf = NULL;
141 for (n = 0; n < nfields; n++) {
142 sf = &fields[n];
143
144 if (n <= 9 && c == '0' + n) {
145 found = true;
146 break;
147 }
148
149 if (n > 9 && (c == 'A' + (n - 10) || c == 'a' + (n - 10))) {
150 found = true;
151 break;
152 }
153 }
154
155 if (!found) {
156 session_printf(s, "Invalid option ({{B}}?{{/B}} for help)\r\n");
157 session_flush(s);
158 continue;
159 }
160
161 get_input:
162 session_printf(s, "{{B}}%s:{{/B}} ", sf->name);
163 session_flush(s);
164
165 switch (sf->type) {
166 case CONFIG_TYPE_STRING:
167 input = session_field_input(s, sf->max, 50, new_data + sf->off,
168 false, 0);
169 session_output(s, "\r\n", 2);
170 session_flush(s);
171 if (input == NULL || s->ending)
172 break;
173
174 if (strlen(input) >= sf->max) {
175 session_printf(s,
176 "%s is too long (%d max, ^C to cancel)", sf->name,
177 sf->max - 1);
178 xfree(&input);
179 goto get_input;
180 }
181 strlcpy(new_data + sf->off, input, sf->max);
182 any_changes = true;
183 break;
184 case CONFIG_TYPE_SHORT:
185 case CONFIG_TYPE_LONG:
186 if (sf->type == CONFIG_TYPE_LONG) {
187 lval = CHARS_TO_LONG(new_data[sf->off],
188 new_data[sf->off + 1], new_data[sf->off + 2],
189 new_data[sf->off + 3]);
190 snprintf(initial, sizeof(initial), "%ld", lval);
191 } else {
192 sval = CHARS_TO_SHORT(new_data[sf->off],
193 new_data[sf->off + 1]);
194 snprintf(initial, sizeof(initial), "%d", sval);
195 }
196 input = session_field_input(s, 10, 10, initial, false, 0);
197 session_output(s, "\r\n", 2);
198 session_flush(s);
199 if (input == NULL || s->ending)
200 break;
201
202 lval = atol(input);
203 if (lval < sf->min) {
204 session_printf(s,
205 "%s must be at least %d (^C to cancel)\r\n", sf->name,
206 sf->min);
207 xfree(&input);
208 goto get_input;
209 }
210 if (lval > sf->max) {
211 session_printf(s,
212 "%s must be less than %d (^C to cancel)\r\n", sf->name,
213 sf->max);
214 xfree(&input);
215 goto get_input;
216 }
217 if (sf->type == CONFIG_TYPE_LONG) {
218 new_data[sf->off] = (lval >> 24) & 0xff;
219 new_data[sf->off + 1] = (lval >> 16) & 0xff;
220 new_data[sf->off + 2] = (lval >> 8) & 0xff;
221 new_data[sf->off + 3] = lval & 0xff;
222 } else {
223 sval = lval;
224 new_data[sf->off] = (sval >> 8) & 0xff;
225 new_data[sf->off + 1] = sval & 0xff;
226 }
227 any_changes = true;
228 break;
229 case CONFIG_TYPE_BOOLEAN:
230 if (new_data[sf->off])
231 session_printf(s, "[Y/n] ");
232 else
233 session_printf(s, "[y/N] ");
234 session_flush(s);
235
236 for (;;) {
237 c = session_input_char(s);
238 if (s->ending)
239 break;
240 if (c == '\r' && new_data[sf->off])
241 c = 'y';
242 else if (c == '\r' && new_data[sf->off] == 0)
243 c = 'n';
244 if (c == 'y' || c == 'Y') {
245 new_data[sf->off] = 1;
246 any_changes = true;
247 session_printf(s, "%c\r\n", c);
248 break;
249 }
250 if (c == 'n' || c == 'N') {
251 new_data[sf->off] = 0;
252 any_changes = true;
253 session_printf(s, "%c\r\n", c);
254 break;
255 }
256 }
257 break;
258 case CONFIG_TYPE_IP: {
259 char ip_str[16];
260 lval = CHARS_TO_LONG(new_data[sf->off], new_data[sf->off + 1],
261 new_data[sf->off + 2], new_data[sf->off + 3]);
262 if (lval == 0)
263 snprintf(initial, sizeof(initial), "0.0.0.0");
264 else {
265 long2ip(lval, ip_str);
266 snprintf(initial, sizeof(initial), ip_str);
267 }
268
269 input = session_field_input(s, 16, 16, initial, false, 0);
270 session_output(s, "\r\n", 2);
271 session_flush(s);
272 if (input == NULL || s->ending)
273 break;
274
275 if (input[0] == '\0') {
276 lval = 0;
277 xfree(&input);
278 } else {
279 lval = ip2long(input);
280 xfree(&input);
281 if (lval == 0) {
282 session_printf(s,
283 "Invalid IP address (^C to cancel)\r\n");
284 goto get_input;
285 }
286 }
287 new_data[sf->off] = (lval >> 24) & 0xff;
288 new_data[sf->off + 1] = (lval >> 16) & 0xff;
289 new_data[sf->off + 2] = (lval >> 8) & 0xff;
290 new_data[sf->off + 3] = lval & 0xff;
291 any_changes = true;
292 break;
293 }
294 }
295
296 if (s->ending) {
297 any_changes = false;
298 goto done;
299 }
300 }
301
302 done:
303 if (any_changes) {
304 *result = new_data;
305 return 0;
306 } else {
307 xfree(&new_data);
308 *result = NULL;
309 return -1;
310 }
311 }
312
313 void
314 view_editor_show(size_t id, char *title)
315 {
316 struct focusable *focusable;
317 struct view_editor *view_editor;
318 Rect bounds, te_bounds;
319 TextStyle style;
320 short width, height;
321 size_t vsize;
322 char *view = NULL;
323 short padding = 10;
324
325 width = 480;
326 height = 250;
327
328 bounds.left = (screenBits.bounds.right - screenBits.bounds.left -
329 width) / 2;
330 bounds.right = bounds.left + width;
331 bounds.top = GetMBarHeight() +
332 ((screenBits.bounds.bottom - height) / 2);
333 bounds.bottom = bounds.top + height;
334
335 view_editor = xmalloczero(sizeof(struct view_editor),
336 "view_editor_show editor");
337 view_editor->view_id = id;
338
339 CtoPstr(title);
340 view_editor->win = NewWindow(0L, &bounds, title, false, noGrowDocProc,
341 (WindowPtr)-1L, true, 0);
342 PtoCstr(title);
343 if (!view_editor->win)
344 panic("Can't create window");
345 SetPort(view_editor->win);
346
347 /* add save button */
348 TextFont(applFont);
349 TextSize(11);
350 bounds.left = view_editor->win->portRect.right - padding - 100;
351 bounds.right = bounds.left + 100;
352 bounds.bottom = view_editor->win->portRect.bottom - padding;
353 bounds.top = bounds.bottom - 20;
354 view_editor->save_button = NewControl(view_editor->win, &bounds,
355 "\pSave", true, 1, 1, 1, pushButProc, 0L);
356
357 /* add text box */
358 bounds.bottom = bounds.top - padding - SCROLLBAR_WIDTH;
359 bounds.top = padding;
360 bounds.left = padding;
361 bounds.right = view_editor->win->portRect.right - SCROLLBAR_WIDTH -
362 padding;
363 te_bounds = bounds;
364 InsetRect(&te_bounds, 2, 2);
365 TextFont(monaco);
366 TextSize(9);
367 view_editor->te = TEStylNew(&te_bounds, &bounds);
368 style.tsFont = monaco;
369 style.tsSize = 9;
370 TESetStyle(doFont | doSize, &style, false, view_editor->te);
371 TEAutoView(true, view_editor->te);
372 TETabWidth = 8;
373 TETabEnable(view_editor->te);
374 TEActivate(view_editor->te);
375
376 vsize = bile_read_alloc(db->bile, DB_TEXT_TYPE, id, &view);
377 if (view) {
378 TESetText(view, vsize, view_editor->te);
379 HLock(view_editor->te);
380 InvalRect(&(*(view_editor->te))->viewRect);
381 HUnlock(view_editor->te);
382 xfree(&view);
383 }
384
385 bounds.left = bounds.right;
386 bounds.right += SCROLLBAR_WIDTH;
387 bounds.bottom++;
388 bounds.top--;
389 view_editor->vert_scroller = NewControl(view_editor->win, &bounds,
390 "\p", true, 1, 1, 1, scrollBarProc, 0L);
391
392 bounds.left = (*(view_editor->te))->viewRect.left - 1;
393 bounds.right = (*(view_editor->te))->viewRect.right + 1;
394 bounds.top = (*(view_editor->te))->viewRect.bottom;
395 bounds.bottom = bounds.top + SCROLLBAR_WIDTH;
396 view_editor->horiz_scroller = NewControl(view_editor->win, &bounds,
397 "\p", true, 1, 1, 1, scrollBarProc, 0L);
398
399 focusable = xmalloczero(sizeof(struct focusable), "focusable");
400 focusable->win = view_editor->win;
401 focusable->cookie = view_editor;
402 focusable->key_down = view_editor_key_down;
403 focusable->mouse_down = view_editor_mouse_down;
404 focusable->update = view_editor_update;
405 focusable->close = view_editor_close;
406 add_focusable(focusable);
407
408 UpdateScrollbarForTE(view_editor->win, view_editor->vert_scroller,
409 view_editor->te, false);
410 UpdateScrollbarForTE(view_editor->win, view_editor->horiz_scroller,
411 view_editor->te, false);
412 }
413
414 void
415 view_editor_key_down(struct focusable *focusable, EventRecord *event)
416 {
417 struct view_editor *view_editor =
418 (struct view_editor *)focusable->cookie;
419 char k = (event->message & charCodeMask);
420
421 if ((event->modifiers & cmdKey) != 0) {
422 switch (k) {
423 case 'c':
424 TECopy(view_editor->te);
425 break;
426 case 'v':
427 TEPaste(view_editor->te);
428 break;
429 case 'x':
430 TECut(view_editor->te);
431 break;
432 }
433 } else
434 TEKey(k, view_editor->te);
435
436 UpdateScrollbarForTE(view_editor->win, view_editor->vert_scroller,
437 view_editor->te, false);
438 UpdateScrollbarForTE(view_editor->win, view_editor->horiz_scroller,
439 view_editor->te, false);
440 }
441
442 void
443 view_editor_mouse_down(struct focusable *focusable, EventRecord *event)
444 {
445 struct view_editor *view_editor =
446 (struct view_editor *)focusable->cookie;
447 Point p;
448 ControlHandle control;
449 Rect r;
450 short val, adj, part;
451
452 p = event->where;
453 GlobalToLocal(&p);
454
455 r = (*(view_editor->te))->viewRect;
456 if (PtInRect(p, &r)) {
457 TEClick(p, ((event->modifiers & shiftKey) != 0),
458 view_editor->te);
459 return;
460 }
461
462 switch (part = FindControl(p, view_editor->win, &control)) {
463 case inButton:
464 if (TrackControl(control, p, 0L)) {
465 if (control == view_editor->save_button)
466 view_editor_save(focusable, NULL);
467 }
468 break;
469 case inUpButton:
470 case inDownButton:
471 case inPageUp:
472 case inPageDown:
473 if (control != view_editor->vert_scroller &&
474 control != view_editor->horiz_scroller)
475 break;
476
477 SetTrackControlTE(view_editor->te);
478 TrackControl(control, p, TrackMouseDownInControl);
479 break;
480 case inThumb:
481 val = GetCtlValue(control);
482 if (TrackControl(control, p, 0L) == 0)
483 break;
484 adj = val - GetCtlValue(control);
485 if (adj != 0) {
486 val -= adj;
487 if (control == view_editor->vert_scroller)
488 TEScroll(0, adj * TEGetHeight(0, 0, view_editor->te),
489 view_editor->te);
490 else if (control == view_editor->horiz_scroller)
491 TEScroll(adj * TEGetWidth(0, view_editor->te), 0,
492 view_editor->te);
493 SetCtlValue(control, val);
494 }
495 break;
496 }
497 }
498
499 void
500 view_editor_update(struct focusable *focusable, EventRecord *event)
501 {
502 struct view_editor *view_editor =
503 (struct view_editor *)focusable->cookie;
504 Rect r;
505
506 r = (*(view_editor->te))->viewRect;
507 FillRect(&r, white);
508 TEUpdate(&r, view_editor->te);
509 InsetRect(&r, -1, -1);
510 FrameRect(&r);
511
512 UpdtControl(view_editor->win, view_editor->win->visRgn);
513 }
514
515 void
516 view_editor_save(struct focusable *focusable, EventRecord *event)
517 {
518 struct view_editor *view_editor =
519 (struct view_editor *)focusable->cookie;
520 size_t len;
521
522 HLock(view_editor->te);
523 HLock((*(view_editor->te))->hText);
524
525 len = (*(view_editor->te))->teLength;
526
527 if (view_editor->view_id == DB_TEXT_MENU_OPTIONS_ID) {
528 if (!main_menu_parse(*(*(view_editor->te))->hText, len)) {
529 HUnlock((*(view_editor->te))->hText);
530 HUnlock(view_editor->te);
531 return;
532 }
533 }
534
535 bile_write(db->bile, DB_TEXT_TYPE, view_editor->view_id,
536 *(*(view_editor->te))->hText, len);
537
538 HUnlock((*(view_editor->te))->hText);
539 HUnlock(view_editor->te);
540
541 if (view_editor->view_id == DB_TEXT_MENU_OPTIONS_ID)
542 main_menu_init();
543
544 view_editor_close(focusable, event);
545 }
546
547 void
548 view_editor_close(struct focusable *focusable, EventRecord *event)
549 {
550 struct view_editor *view_editor =
551 (struct view_editor *)focusable->cookie;
552
553 TEDispose(view_editor->te);
554 destroy_focusable(focusable);
555 xfree(&view_editor);
556 }