From 001d67553b3181abab209189984bdc1a0bd5032e Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Sat, 1 Aug 2020 12:47:48 -0400 Subject: [PATCH] re-arrange a bit to make cli scope management easier. --- base.py | 29 ++++++++------- cli.py | 38 ++++++++++++++++--- colors.py | 38 +++++++++---------- control.py | 19 ++++++---- menu.py | 106 +++++++++++++++++++++++++++++++++++++++++++++++++---- rect.py | 11 +----- sound.py | 14 +++---- utils.py | 30 +++++++++++++++ window.py | 3 +- 9 files changed, 216 insertions(+), 72 deletions(-) create mode 100644 utils.py diff --git a/base.py b/base.py index 2859264..ee0d027 100644 --- a/base.py +++ b/base.py @@ -1,21 +1,12 @@ import struct from bisect import bisect_left from rect import * +from utils import * -# helper functions -def str_to_bytes(text): - if isinstance(text, str): return text.encode("macroman") - if isinstance(text, bytes): return text - if isinstance(text, bytearray): return bytes(text) - raise TypeError("Bad text type: {}".format(type(text))) - - -def make_string(text, rType=None): - if not rType: rType = rPString - if type(text) == rType: return text - if type(text) in (str, bytes, bytearray): return rType(text) - raise TypeError("Bad text type: {}".format(type(text))) - +__all__ = ["rObject", "rText", "rTextBlock", "rTextForLETextBox2", + "rAlertString", "rErrorString", "rComment", "rPString", + "rCString", "rWString", "rC1InputString", "rStringList", + "rTwoRects", "rRectList"] #define KeyEquiv array[1]{ char; char; _mybase_ word; _mybase_ word; } @@ -183,6 +174,16 @@ class rTextObject(rObject): map = rTextObject._map return "".join([map[x] if x in map else chr(x) for x in bb]) + @classmethod + def make_string(cls, text): + rType = cls + # if not rType: rType = rPString + if type(text) == rType: return text + if type(text) in (str, bytes, bytearray): return rType(text) + raise TypeError("Bad text type: {}".format(type(text))) + + + def __init__(self, text, *, id=None, attr=None): diff --git a/cli.py b/cli.py index 4c9305c..aba46aa 100644 --- a/cli.py +++ b/cli.py @@ -4,18 +4,44 @@ import io import argparse import time -from base import * -from window import * -from control import * -from menu import * -from rect import rect, point, size +from base import rObject + +# from base import * +# from window import * +# from control import * +# from menu import * +# from sound import * +# from rect import rect, point, size + + +def rez_scope(): + import base + import window + import control + import menu + import sound + import rect + + scope = {} + for mod in (base, window, control, menu, sound, rect): + + if hasattr(mod, '__all__'): keys = mod.__all__ + else: keys = [x for x in dir(mod) if x[0] != '_'] + + for key in keys: + scope[key] = getattr(mod, key) + + return scope + + def execute(filename): + scope = rez_scope() try: with open(filename, 'r', encoding="utf-8") as f: src = f.read() code = compile(src, filename, "exec") - exec(code, None, {}) + exec(code, {}, scope) return True pass except Exception as e: diff --git a/colors.py b/colors.py index 7c7238a..1813c06 100644 --- a/colors.py +++ b/colors.py @@ -1,28 +1,28 @@ -class Color: +class _Color: __slots__ = ("value") def __init__(self, value): self.value = value -# 640 mode colors +# 640 mode _colors -Transparent = Color(None) -Black = Color(0x00) -Blue = Color(0x01) -Olive = Color(0x02) -Gray1 = Color(0x03) -Red = Color(0x04) -Purple = Color(0x05) -Orange = Color(0x06) -Salmon = Color(0x07) -Green = Color(0x08) -Turquoise = Color(0x09) -BrightGreen = Color(0x0a) -DullGreen = Color(0x0b) -Gray2 = Color(0x0c) -LightBlue = Color(0x0d) -Yellow = Color(0x0e) -White = Color(0x0f) +Transparent = _Color(None) +Black = _Color(0x00) +Blue = _Color(0x01) +Olive = _Color(0x02) +Gray1 = _Color(0x03) +Red = _Color(0x04) +Purple = _Color(0x05) +Orange = _Color(0x06) +Salmon = _Color(0x07) +Green = _Color(0x08) +Turquoise = _Color(0x09) +BrightGreen = _Color(0x0a) +DullGreen = _Color(0x0b) +Gray2 = _Color(0x0c) +LightBlue = _Color(0x0d) +Yellow = _Color(0x0e) +White = _Color(0x0f) # synonyms Grey1 = Gray1 diff --git a/control.py b/control.py index 475ab2f..db517bc 100644 --- a/control.py +++ b/control.py @@ -1,10 +1,15 @@ -from base import * +from base import rObject, rList, rPString, rTextForLETextBox2 import struct from rect import * from colors import * +from utils import * - +__all__ = [ + "rControlTemplate", "rControlList", "rSimpleButton", + "rCheckControl", "rRadioControl", "rThermometerControl", + "rRectangleControl", "rStatTextControl" +] # /*-------------------------------------------------------*/ # /* Control List Descriptors @@ -25,7 +30,7 @@ from colors import * # /*-------------------------------------------------------*/ # #define ctlInvis $0080 # #define ctlVisible $0000 -# #Define CtlInactive $FF00# +# #Define CtlInactive $FF00 # # # /*-------------------------------------------------------*/ @@ -87,7 +92,7 @@ class rSimpleButton(rControlTemplate): if title: moreFlags |= 0x02 # title is resource id - self.title = make_string(title) + self.title = rPString.make_string(title) self.flags = flags self.moreFlags = moreFlags @@ -175,7 +180,7 @@ class rCheckControl(rControlTemplate): if title: moreFlags |= 0x02 # title is resource id - self.title = make_string(title) + self.title = rPString.make_string(title) self.flags = flags self.moreFlags = moreFlags @@ -269,7 +274,7 @@ class rRadioControl(rControlTemplate): if title: moreFlags |= 0x02 # title is resource id - self.title = make_string(title) + self.title = rPString.make_string(title) self.flags = flags self.moreFlags = moreFlags @@ -577,7 +582,7 @@ class rStatTextControl(rControlTemplate): elif fullJust: just = 2 elif rightJust: just = -1 - self.text = make_string(text, rTextForLETextBox2) + self.text = rTextForLETextBox2.make_string(text) self.flags = flags self.moreFlags = moreFlags diff --git a/menu.py b/menu.py index 8a0a1c4..bb2cf4e 100644 --- a/menu.py +++ b/menu.py @@ -1,15 +1,44 @@ from base import * +from utils import * import struct -__all__ = ['rMenuBar', 'rMenu', 'rMenuItem'] +__all__ = [ + 'rMenuBar', + 'rMenu', + 'rMenuItem', -def to_char_string(x): + 'UndoMenuItem', + 'CutMenuItem', + 'CopyMenuItem', + 'PasteMenuItem', + 'ClearMenuItem', + 'CloseMenuItem', + 'DividerMenuItem' +] + +# see: +# Programmer's Reference for System 6 (Ch 13 Menu Manager Update, pg 103+) +# TB Vol 3 Chapter 37 +# TB Vol 1 Chapter 13 + +# TODO - menu item icon support (system 6) rItemStruct + +# A menu ID must be unique for each menu; that is, no two menus +# can have the same ID or the system will fall. Similarly, no two +# items can have the same Item ID. + +_menu_ids = {} +_menu_item_ids = {} + + + +def _to_char_string(x): if not x: return '""' if x in (0x0a, 0x0d): return "\\n" if x == ord('"'): return "\"" if x >= 32 and x < 0x7e: return '"' + chr(x) + '"' - return "\\x{:02x}".format(x) + return "\\${:02x}".format(x) class rMenuBar(rObject): rName = "rMenuBar" @@ -39,7 +68,7 @@ class rMenuBar(rObject): return rv - +# valid menu ids: $0001-$fffe class rMenu(rObject): rName = "rMenu" rType = 0x8009 @@ -59,7 +88,7 @@ class rMenu(rObject): **kwargs ): super().__init__(id, attr) - self.title = make_string(title, rPString) + self.title = rPString.make_string(title) self.children = children[:] self.menuID = menuID @@ -105,6 +134,16 @@ class rMenu(rObject): return rv +miUndo = 0xfa +miCut = 0xfb +miCopy = 0xfc +miPaste = 0xfd +miClear = 0xfe +miClose = 0xff + + +# valid menu item ids: $0100-$fffe + class rMenuItem(rObject): rName = "rMenuItem" rType = 0x800a @@ -130,7 +169,7 @@ class rMenuItem(rObject): **kwargs): super().__init__(id, attr) - self.title = make_string(title, rPString) + self.title = rPString.make_string(title) flags |= 0x8000 # title ref is resource if kwargs.get("bold"): flags |= 0x0001 @@ -142,6 +181,9 @@ class rMenuItem(rObject): if kwargs.get("outline"): flags |= 0x0800 if kwargs.get("shadow"): flags |= 0x1000 + # bit 15 of flags set if title ref is actually an + # item struct ref. + self.flags = flags self.itemChar = 0 @@ -191,10 +233,58 @@ class rMenuItem(rObject): "\t${:04x} /* title ref */" ).format( itemID, - to_char_string(self.itemChar), - to_char_string(self.altItemChar), + _to_char_string(self.itemChar), + _to_char_string(self.altItemChar), self.checkMark, self.flags, self.title.get_id() ) + +def _singleton(func): + value = None + def inner(): + nonlocal value + if not value: value = func() + return value + return inner + + +def DividerMenuItem(): + return rMenuItem("-", flags = 0x0080) # disabled + +def EditMenu(*children, **kwargs): + return rMenu(" Edit ", + UndoMenuItem(), + CutMenuItem(), + CopyMenuItem(), + PasteMenuItem(), + ClearMenuItem(), + *children, + **kwargs + ) + +@ _singleton +def UndoMenuItem(): + # normally underlined.... + return rMenuItem("Undo", "Zz", itemID = miUndo) + +@ _singleton +def CutMenuItem(): + return rMenuItem("Cut", "Xx", itemID = miCut) + +@ _singleton +def CopyMenuItem(): + return rMenuItem("Copy", "Cc", itemID = miCopy) + +@ _singleton +def PasteMenuItem(): + return rMenuItem("Paste", "Vv", itemID = miPaste) + +@ _singleton +def ClearMenuItem(): + return rMenuItem("Clear", "", itemID = miClear) + +@ _singleton +def CloseMenuItem(): + return rMenuItem("Close", "Ww", itemID = miClose) diff --git a/rect.py b/rect.py index 58373ef..9b1d078 100644 --- a/rect.py +++ b/rect.py @@ -1,6 +1,6 @@ # point(h,v) or point(x=.., y=..) -__all__ = ['point', 'rect', 'size', 'format_rect', 'format_point', 'format_size'] +__all__ = ['point', 'rect', 'size'] def all_defined(*args): return args.count(None)==0 def all_none(*args): return args.count(None)==len(args) @@ -105,12 +105,3 @@ def rect(*args, raise ValueError("bad rect parameter") - -def format_rect(x): - return "{{ {:d}, {:d}, {:d}, {:d} }}".format(*x) - -def format_point(x): - return "{{ {:d}, {:d} }}".format(*x) - -def format_size(x): - return "{{ {:d}, {:d} }}".format(*x) diff --git a/sound.py b/sound.py index 491ae2b..6ed646b 100644 --- a/sound.py +++ b/sound.py @@ -72,14 +72,14 @@ def open_audio(file): class rSoundSample(rObject): - """ - filename: input file to read. format is .wav, .au, .aiff, or .aifc - pitch: audio pitch, if this is a note. specify hz (eg 261.63) or name (eg c4) - rate: down/upsample audio to this rate (eg 26320) - channel: stereo channel + """ + filename: input file to read. format is .wav, .au, .aiff, or .aifc + pitch: audio pitch, if this is a note. specify hz (eg 261.63) or name (eg c4) + rate: down/upsample audio to this rate (eg 26320) + channel: stereo channel - Native samples are 26320 khz, c4 (261.63 hz) - """ + Native samples are 26320 khz, c4 (261.63 hz) + """ rName = "rSoundSample" rType = 0x8024 diff --git a/utils.py b/utils.py new file mode 100644 index 0000000..d4f3403 --- /dev/null +++ b/utils.py @@ -0,0 +1,30 @@ + + + +# __all__ = ("str_to_bytes", "make_string", "format_rect", "format_point", "format_size") + +# helper functions +def str_to_bytes(text): + if isinstance(text, str): return text.encode("macroman") + if isinstance(text, bytes): return text + if isinstance(text, bytearray): return bytes(text) + raise TypeError("Bad text type: {}".format(type(text))) + + + +def format_rect(x): + return "{{ {:d}, {:d}, {:d}, {:d} }}".format(*x) + +def format_point(x): + return "{{ {:d}, {:d} }}".format(*x) + +def format_size(x): + return "{{ {:d}, {:d} }}".format(*x) + + +# def make_string(text, rType=None): +# if not rType: rType = rPString +# if type(text) == rType: return text +# if type(text) in (str, bytes, bytearray): return rType(text) +# raise TypeError("Bad text type: {}".format(type(text))) + diff --git a/window.py b/window.py index 96a688f..6dbc40f 100644 --- a/window.py +++ b/window.py @@ -1,5 +1,6 @@ from base import * from control import rControlTemplate, rControlList +from utils import * import struct @@ -57,7 +58,7 @@ class rWindParam1(rObject): super().__init__(id=id, attr=attr) self.frameBits = frameBits - if title: self.title = make_string(title) + if title: self.title = rPString.make_string(title) self.zoomRect=zoomRect self.color = None self.origin = origin