AmendHub

Download:

jcs

/

subtext

/

amendments

/

56

settings: Start on settings windows


jcs made amendment 56 over 2 years ago
--- settings.c Tue Jan 18 17:05:20 2022 +++ settings.c Tue Jan 18 17:05:20 2022 @@ -0,0 +1,333 @@ +/* + * Copyright (c) 2021-2022 joshua stein <jcs@jcs.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdio.h> +#include <string.h> + +#include "subtext.h" +#include "db.h" +#include "settings.h" +#include "tetab.h" +#include "util.h" + +enum { + CONFIG_TYPE_STRING, + CONFIG_TYPE_SHORT +}; + +struct config_field { + char name[32]; + short type; + unsigned short off; + unsigned short min; + unsigned short max; + short ditl_id; +} config_fields[] = { + { "BBS Name", CONFIG_TYPE_STRING, + offsetof(struct config, name), 1, 32, SETTINGS_BBS_NAME_ID }, + { "Phone Number", CONFIG_TYPE_STRING, + offsetof(struct config, phone_number), 1, 16, SETTINGS_PHONE_ID }, + { "Location", CONFIG_TYPE_STRING, + offsetof(struct config, location), 1, 64, SETTINGS_LOCATION_ID }, + { "Hostname", CONFIG_TYPE_STRING, + offsetof(struct config, hostname), 1, 32, SETTINGS_HOSTNAME_ID }, + { "Telnet Port", CONFIG_TYPE_SHORT, + offsetof(struct config, telnet_port), 1, 65535, SETTINGS_TELNET_PORT_ID } +}; + +struct view_editor { + WindowPtr win; + size_t view_id; + TEHandle te; + ControlHandle scroller; +}; + +void view_editor_key_down(struct focusable *focusable, EventRecord *event); +void view_editor_mouse_down(struct focusable *focusable, + EventRecord *event); +void view_editor_update(struct focusable *focusable, EventRecord *event); +void view_editor_close(struct focusable *focusable, EventRecord *event); + +void +settings_edit(void) +{ + Str255 txt; + Handle ihandle; + struct config new_config; + size_t len; + DialogPtr dlg; + Rect irect; + short done = 0, itype, hit, n; + + new_config = db->config; + + if ((dlg = GetNewDialog(SETTINGS_DLOG_ID, nil, (WindowPtr)-1)) == NULL) + panic("Can't find settings DLOG %d", SETTINGS_DLOG_ID); + + for (n = 0; n < nitems(config_fields); n++) { + struct config_field *cf = &config_fields[n]; + short sval; + + GetDItem(dlg, cf->ditl_id, &itype, &ihandle, &irect); + + switch (cf->type) { + case CONFIG_TYPE_STRING: + strlcpy((char *)&txt, (char *)&new_config + cf->off, + sizeof(txt)); + break; + case CONFIG_TYPE_SHORT: + sval = (((char *)&new_config)[cf->off] << 8) | + ((char *)&new_config)[cf->off + 1]; + snprintf((char *)&txt, sizeof(txt), "%d", sval); + break; + } + + CtoPstr(txt); + SetIText(ihandle, txt); + } + + ShowWindow(dlg); + + while (!done) { +get_input: + ModalDialog(nil, &hit); + switch (hit) { + case OK: + done = 1; + break; + case Cancel: + goto cancel; + } + + if (!done) + continue; + + for (n = 0; n < nitems(config_fields); n++) { + struct config_field *cf = &config_fields[n]; + long lval; + short sval; + + GetDItem(dlg, cf->ditl_id, &itype, &ihandle, &irect); + GetIText(ihandle, txt); + PtoCstr(txt); + + switch (cf->type) { + case CONFIG_TYPE_STRING: + if (strlen((char *)txt) >= cf->max) { + warn("%s is too long (%d max)", cf->name, cf->max - 1); + done = 0; + goto get_input; + } + strlcpy((char *)&new_config + cf->off, (char *)&txt, + cf->max); + break; + case CONFIG_TYPE_SHORT: + lval = atol((char *)txt); + if (lval < cf->min) { + warn("%s must be at least %d", cf->name, cf->min); + done = 0; + goto get_input; + } + if (lval > cf->max) { + warn("%s must be less than %d", cf->name, cf->max); + done = 0; + goto get_input; + } + sval = lval; + memcpy(&new_config + cf->off, &sval, sizeof(short)); + break; + } + } + } + + db->config = new_config; + db_config_save(db); + +cancel: + DisposeDialog(dlg); +} + +void +view_editor_show(size_t id, char *title) +{ + struct focusable *focusable; + struct view_editor *view_editor; + Rect bounds, te_bounds; + TextStyle style; + short width, height, fh, hit; + size_t vsize; + char *view = NULL; + TEHandle te; + short padding = 10; + bool done; + + vsize = bile_read_alloc(db->bile, DB_TEXT_TYPE, id, &view); +// if (vsize == 0) +// return; + + width = 480; + height = 250; + + bounds.left = (screenBits.bounds.right - screenBits.bounds.left - + width) / 2; + bounds.right = bounds.left + width; + bounds.top = GetMBarHeight() + + ((screenBits.bounds.bottom - height) / 2); + bounds.bottom = bounds.top + height; + + view_editor = xmalloczero(sizeof(struct view_editor)); + view_editor->view_id = id; + + CtoPstr(title); + view_editor->win = NewWindow(0L, &bounds, title, false, noGrowDocProc, + (WindowPtr)-1L, true, 0); + PtoCstr(title); + if (!view_editor->win) + panic("Can't create window"); + SetPort(view_editor->win); + + bounds.top = padding; + bounds.left = padding; + fh = FontHeight(monaco, 9); + bounds.bottom = view_editor->win->portRect.bottom - padding; + bounds.right = view_editor->win->portRect.right - SCROLLBAR_WIDTH - + padding; + te_bounds = bounds; + InsetRect(&te_bounds, 2, 2); + TextFont(monaco); + TextSize(9); + view_editor->te = TEStylNew(&te_bounds, &bounds); + style.tsFont = monaco; + style.tsSize = 9; + TESetStyle(doFont | doSize, &style, false, view_editor->te); + TEAutoView(true, view_editor->te); + TETabEnable(view_editor->te); + + if (view) + free(view); + + bounds.left = bounds.right; + bounds.right += SCROLLBAR_WIDTH; + bounds.bottom++; + bounds.top--; + view_editor->scroller = NewControl(view_editor->win, &bounds, "\p", + true, 1, 1, 1, scrollBarProc, 0L); + + focusable = xmalloczero(sizeof(struct focusable)); + focusable->win = view_editor->win; + focusable->cookie = view_editor; + focusable->key_down = view_editor_key_down; + focusable->mouse_down = view_editor_mouse_down; + focusable->update = view_editor_update; + focusable->close = view_editor_close; + show_focusable(focusable); +} + +void +view_editor_key_down(struct focusable *focusable, EventRecord *event) +{ + struct view_editor *view_editor = + (struct view_editor *)focusable->cookie; + char k; + + k = (event->message & charCodeMask); + TEKey(k, view_editor->te); + UpdateScrollbarForTE(view_editor->scroller, view_editor->te, + false); +} + +void +view_editor_mouse_down(struct focusable *focusable, EventRecord *event) +{ + struct view_editor *view_editor = + (struct view_editor *)focusable->cookie; + Point p; + ControlHandle control; + Rect r; + short val, adj, page, was_selected, part, i; + + p = event->where; + GlobalToLocal(&p); + + r = (*(view_editor->te))->viewRect; + if (PtInRect(p, &r)) { + TEClick(p, ((event->modifiers & shiftKey) != 0), + view_editor->te); + return; + } + + switch (part = FindControl(p, view_editor->win, &control)) { + case inButton: + TextFont(applFont); + TextSize(11); + if (TrackControl(control, p, 0L)) + ; + break; + case inUpButton: + case inDownButton: + case inPageUp: + case inPageDown: + if (control == view_editor->scroller) + SetTrackControlTE(view_editor->te); + else + break; + TrackControl(control, p, TrackMouseDownInControl); + break; + case inThumb: + val = GetCtlValue(control); + if (TrackControl(control, p, 0L) == 0) + break; + adj = val - GetCtlValue(control); + if (adj != 0) { + val -= adj; + if (control == view_editor->scroller) + TEScroll(0, adj * TEGetHeight(0, 0, view_editor->te), + view_editor->te); + SetCtlValue(control, val); + } + break; + } +} + +void +view_editor_update(struct focusable *focusable, EventRecord *event) +{ + struct view_editor *view_editor = + (struct view_editor *)focusable->cookie; + Rect r; + + r = (*(view_editor->te))->viewRect; + FillRect(&r, white); + TEUpdate(&r, view_editor->te); + InsetRect(&r, -1, -1); + FrameRect(&r); + + UpdtControl(view_editor->win, view_editor->win->visRgn); +} + +void +view_editor_close(struct focusable *focusable, EventRecord *event) +{ + struct view_editor *view_editor = + (struct view_editor *)focusable->cookie; + + close_focusable(focusable); + DisposeWindow(view_editor->win); + TEDispose(view_editor->te); + DisposHandle(view_editor->scroller); + free(focusable); + free(view_editor); +} --- settings.h Tue Jan 18 08:07:32 2022 +++ settings.h Tue Jan 18 08:07:32 2022 @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2021-2022 joshua stein <jcs@jcs.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __SETTINGS_H__ +#define __SETTINGS_H__ + +void settings_edit(void); +void view_editor_show(size_t id, char *title); + +#endif --- tetab.c Fri Oct 29 17:17:44 2021 +++ tetab.c Fri Oct 29 17:17:44 2021 @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2021 joshua stein <jcs@jcs.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Implements tab spacing in TextEdit controls with a fixed-width font. + * Only works on styled TextEdit controls (created with TEStylNew) and + * assumes that functions get called with a text buffer that starts at the + * beginning of the line. + * + * Usage is per-TextEdit control, so these hooks need to be established + * on all controls where tabs should be expanded: + * + * TEHandle te; + * ProcPtr teproc; + * + * [..] + * te = TEStylNew(&bounds, &bounds); + * TEAutoView(true, te); + * TETabEnable(te); + * + * The tab width is set to 4 by default in the TETabWidth global. + * + * Thanks to Ari Halberstadt for their 1989 Usenet post on + * comp.sys.mac.programmer with HitTestHook assembly. + */ + +#include <Script.h> +#include <SetUpA4.h> + +#include "tetab.h" + +pascal void TETabDrawHook(void); +pascal void TETabWidthHook(void); +pascal void TETabHitTestHook(void); + +short TETabWidth = 4; + +void +TETabEnable(TEHandle te) +{ + ProcPtr teproc; + + TEAutoView(true, te); + teproc = StripAddress(TETabWidthHook); + TECustomHook(intWidthHook, &teproc, te); + teproc = StripAddress(TETabDrawHook); + TECustomHook(intDrawHook, &teproc, te); + teproc = StripAddress(TETabHitTestHook); + TECustomHook(intHitTestHook, &teproc, te); +} + +pascal void +TETabDrawHook(void) +{ + /* entry registers, in order */ + short offset; + short length; + Ptr text; + + /* locals */ + short i, cwidth, cpos, space, startdraw; + char c; + + asm { + movem.l d3-d7/a0-a3, -(a7) + move.w d0, offset + move.w d1, length + move.l a0, text + } + SetUpA4(); + + cwidth = CharWidth(' '); + + /* + * This startdraw stuff is to try to draw long blocks of non-tab text + * with one call to DrawText() instead of DrawChar()ing every character + * one at a time, for speed. + */ + for (i = 0, cpos = 0, startdraw = 0; i < length; i++) { + c = text[offset + i]; + + if (c == '\t') { + if (i - startdraw) + DrawText(text, startdraw, i - startdraw); + + space = (TETabWidth - (cpos % TETabWidth)); + if (!space) + space = TETabWidth; + cpos += space; + Move(cwidth * space, 0); + startdraw = i + 1; + } else { + cpos++; + } + } + + if (startdraw != i) + DrawText(text, startdraw, i - startdraw); + + /* return nothing */ + + /* restore registers */ + RestoreA4(); + asm { + movem.l (a7)+, d3-d7/a0-a3 + } +} + +pascal void +TETabWidthHook(void) +{ + /* entry registers, in order */ + short length; + short offset; + Ptr text; + + /* exit */ + short width; + + short i, cwidth, cpos, space; + char c; + + asm { + movem.l d3-d7/a0-a3, -(a7) + move.w d0, length + move.w d1, offset + move.l a0, text + } + SetUpA4(); + + cwidth = CharWidth(' '); + + for (i = 0, cpos = 0; i < length; i++) { + c = text[offset + i]; + + if (c == '\t') { + space = (TETabWidth - (cpos % TETabWidth)); + if (!space) + space = TETabWidth; + cpos += space; + } else { + cpos++; + } + } + + width = cwidth * cpos; + + /* return length */ + asm { + move.w width, d1 + } + + /* restore registers */ + RestoreA4(); + asm { + movem.l (a7)+, d3-d7/a0-a3 + } +} + +pascal void +TETabHitTestHook(void) +{ + /* entry registers, in order */ + short length; + short poffset; + Ptr text; + + /* exit */ + short width; + short offset = 0; + short leftside = 0; + + /* locals */ + short i, cwidth, cpos, wpos, space, found; + char c; + + asm { + movem.l d3-d7/a0-a3, -(a7) + move.w d0, length + move.w d1, poffset + move.l a0, text + } + SetUpA4(); + + cwidth = CharWidth(' '); + + for (i = 0, wpos = 0, cpos = 0, found = 0; i < length; i++) { + c = text[i]; + + if (c == '\t') { + space = (TETabWidth - (wpos % TETabWidth)); + if (!space) + space = TETabWidth; + wpos += space; + } else if (c == '\r') + break; + else + wpos++; + + cpos++; + + if (poffset <= (wpos * cwidth)) { + found = 1; + break; + } + } + + /* put found in hi word */ + width = (wpos * cwidth) | (found << 8); + offset = cpos; + leftside = (poffset < (width - (cwidth / 2))); + + asm { + move.w width, d0 + move.w offset, d1 + move.w leftside, d2 + } + + /* restore registers */ + RestoreA4(); + asm { + movem.l (a7)+, d3-d7/a0-a3 + } +} --- tetab.h Thu Oct 21 14:04:42 2021 +++ tetab.h Thu Oct 21 14:04:42 2021 @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2021 joshua stein <jcs@jcs.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +void TETabEnable(TEHandle te); +extern short TETabWidth;