Thun/joy/gui/main.py

141 lines
4.0 KiB
Python
Executable File

#!/usr/bin/env python
# -*- coding: utf-8 -*-
('''\
Joypy - Copyright © 2018 Simon Forman
'''
'This program comes with ABSOLUTELY NO WARRANTY; for details right-click "warranty".'
' This is free software, and you are welcome to redistribute it under certain conditions;'
' right-click "sharing" for details.'
' Right-click on these commands to see docs on UI commands: key_bindings mouse_bindings')
import logging, os, pickle, sys
from textwrap import dedent
from ConfigParser import RawConfigParser
from joy.gui.utils import init_home, argparser, FileFaker
args = argparser.parse_args()
JOY_HOME = args.joy_home
repo = init_home(JOY_HOME)
_log = logging.getLogger(__name__)
logging.basicConfig(
format='%(asctime)-15s %(levelname)s %(name)s %(message)s',
filename=os.path.join(JOY_HOME, 'thun.log'),
level=logging.INFO,
)
_log.info('Starting with JOY_HOME=%s', JOY_HOME)
from joy.gui.textwidget import TextViewerWidget, tk, get_font
from joy.gui.world import StackDisplayWorld
from joy.library import initialize
from joy.utils.stack import stack_to_string
cp = RawConfigParser()
cp.optionxform = str # Don't mess with uppercase.
with open(os.path.join(args.joy_home, 'thun.config')) as f:
cp.readfp(f)
GLOBAL_COMMANDS = dict(cp.items('key bindings'))
def repo_relative_path(path):
return os.path.relpath(
path,
os.path.commonprefix((repo.controldir(), path))
)
def commands():
# pylint: disable=unused-variable
def key_bindings(*args):
commands = [ # These are bound in the TextViewerWidget.
'Control-Enter - Run the selection as Joy code, or if there\'s no selection the line containing the cursor.',
'F3 - Copy selection to stack.',
'Shift-F3 - Cut selection to stack.',
'F4 - Paste item on top of stack to insertion cursor.',
'Shift-F4 - Pop and paste top of stack to insertion cursor.',
]
for key, command in GLOBAL_COMMANDS.iteritems():
commands.append('%s - %s' % (key.lstrip('<').rstrip('>'), command))
print '\n'.join([''] + sorted(commands))
return args
def mouse_bindings(*args):
print dedent('''
Mouse button chords (to cancel a chord, click the third mouse button.)
Left - Point, sweep selection
Left-Middle - Copy the selection, place text on stack
Left-Right - Run the selection as Joy code
Middle - Paste selection (bypass stack); click and drag to scroll.
Middle-Left - Paste from top of stack, preserve
Middle-Right - Paste from top of stack, pop
Right - Execute command word under mouse cursor
Right-Left - Print docs of command word under mouse cursor
Right-Middle - Lookup word (kinda useless now)
''')
return args
def reset_log(*args):
log.delete('0.0', tk.END)
print __doc__
return args
def show_log(*args):
log_window.wm_deiconify()
log_window.update()
return args
def grand_reset(s, e, d):
stack = world.load_stack() or ()
log.reset()
t.reset()
return stack, e, d
return locals()
STACK_FN = os.path.join(JOY_HOME, 'stack.pickle')
REL_STACK_FN = repo_relative_path(STACK_FN)
JOY_FN = os.path.join(JOY_HOME, 'scratch.txt')
LOG_FN = os.path.join(JOY_HOME, 'log.txt')
D = initialize()
D.update(commands())
world = StackDisplayWorld(repo, STACK_FN, REL_STACK_FN, dictionary=D)
defaults = dict(width=80, height=25)
t = TextViewerWidget(world, **defaults)
log_window = tk.Toplevel()
log_window.protocol("WM_DELETE_WINDOW", log_window.withdraw)
log = TextViewerWidget(world, log_window, **defaults)
FONT = get_font('Iosevka', size=14) # Requires Tk root already set up.
log.init('Log', LOG_FN, repo_relative_path(LOG_FN), repo, FONT)
t.init('Joy - ' + JOY_HOME, JOY_FN, repo_relative_path(JOY_FN), repo, FONT)
for event, command in GLOBAL_COMMANDS.items():
callback = lambda _, _command=command: world.interpret(_command)
t.bind(event, callback)
log.bind(event, callback)
def main():
sys.stdout, old_stdout = FileFaker(log), sys.stdout
try:
t.mainloop()
finally:
sys.stdout = old_stdout
return 0
if __name__ == '__main__':
main()